【Rust】——编写自动化测试(一)
🎃个人专栏:
🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客
🐳Java基础:Java基础_IT闫的博客-CSDN博客
🐋c语言:c语言_IT闫的博客-CSDN博客
🐟MySQL:数据结构_IT闫的博客-CSDN博客
🐠数据结构:数据结构_IT闫的博客-CSDN博客
💎C++:C++_IT闫的博客-CSDN博客
🥽C51单片机:C51单片机(STC89C516)_IT闫的博客-CSDN博客
💻基于HTML5的网页设计及应用:基于HTML5的网页设计及应用_IT闫的博客-CSDN博客
🥏python:python_IT闫的博客-CSDN博客
🐠离散数学:离散数学_IT闫的博客-CSDN博客
🥽Linux:Linux_Y小夜的博客-CSDN博客
🚝Rust:Rust_Y小夜的博客-CSDN博客
欢迎收看,希望对大家有用!
目录
🎯编写和运行测试
🎃测试(函数)
🎃解剖测试函数
🎯 断言(Assert)
🎃使用assert!宏检查测试结果
🎃使用assert_eq!和assert_ne!测试相等性
🎯自定义错误信息
🎯使用should_panic检查恐慌
🎃让should_panic更加精准
🎯在测试中使用Result,e>
🎯编写和运行测试
🎃测试(函数)
Rust 中的测试函数是用来验证非测试代码是否是按照期望的方式运行的。测试函数体通常执行如下三种操作:
- 设置任何所需的数据或状态
- 运行需要测试的代码
- 断言其结果是我们所期望的
🎃解剖测试函数
测试成功:
Rust 中的测试就是一个带有
test属性注解的函数。属性(attribute)是关于 Rust 代码片段的元数据;第五章中结构体中用到的derive属性就是一个例子。为了将一个函数变成测试函数,需要在fn行之前加上#[test]。当使用cargo test命令运行测试时,Rust 会构建一个测试执行程序用来调用被标注的函数,并报告每一个测试是通过还是失败。每次使用 Cargo 新建一个库项目时,它会自动为我们生成一个测试模块和一个测试函数。这个模块提供了一个编写测试的模板,为此每次开始新项目时不必去查找测试函数的具体结构和语法了。因为这样当然你也可以额外增加任意多的测试函数以及测试模块。
实际编写测试代码之前,让我们先通过尝试那些自动生成的测试模版来探索测试是如何工作的。接着,我们会写一些真正的测试,调用我们编写的代码并断言它们的行为的正确性。
$ cargo new adder --libCreated library `adder` project $ cd adder#[cfg(test)] mod tests {#[test]fn it_works() {let result = 2 + 2;assert_eq!(result, 4);} }现在让我们暂时忽略
tests模块和#[cfg(test)]注解并只关注函数本身。注意fn行之前的#[test]:这个属性表明这是一个测试函数,这样测试执行者就知道将其作为测试处理。tests模块中也可以有非测试的函数来帮助我们建立通用场景或进行常见操作,必须每次都标明哪些函数是测试。测试失败:
当测试函数中出现 panic 时测试就失败了。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败。
#[cfg(test)] mod tests {#[test]fn exploration() {assert_eq!(2 + 2, 4);}#[test]fn another() {panic!("Make this test fail");} }
🎯 断言(Assert)
🎃使用assert!宏检查测试结果
assert!宏由标准库提供,在希望确保测试中一些条件为true时非常有用。需要向assert!宏提供一个求值为布尔值的参数。如果值是true,assert!什么也不做,同时测试会通过。如果值为false,assert!调用panic!宏,这会导致测试失败。assert!宏帮助我们检查代码是否以期望的方式运行。#[derive(Debug)] struct Rectangle {width: u32,height: u32, }impl Rectangle {fn can_hold(&self, other: &Rectangle) -> bool {self.width > other.width && self.height > other.height} }
🎃使用assert_eq!和assert_ne!测试相等性
测试功能的一个常用方法是将需要测试代码的值与期望值做比较,并检查是否相等。可以通过向
assert!宏传递一个使用==运算符的表达式来做到。不过这个操作实在是太常见了,以至于标准库提供了一对宏来更方便的处理这些操作 ——assert_eq!和assert_ne!。这两个宏分别比较两个值是相等还是不相等。当断言失败时它们也会打印出这两个值具体是什么,以便于观察测试 为什么 失败,而assert!只会打印出它从==表达式中得到了false值,而不是打印导致false的两个值。pub fn add_two(a: i32) -> i32 {a + 2 }#[cfg(test)] mod tests {use super::*;#[test]fn it_adds_two() {assert_eq!(4, add_two(2));} }我们传递给
assert_eq!宏的第一个参数4,它等于调用add_two(2)的结果。测试中的这一行test tests::it_adds_two ... ok中ok表明测试通过!在代码中引入一个 bug 来看看使用
assert_eq!的测试失败是什么样的。修改add_two函数的实现使其加3:pub fn add_two(a: i32) -> i32 {a + 3 }$ cargo testCompiling adder v0.1.0 (file:///projects/adder)Finished test [unoptimized + debuginfo] target(s) in 0.61sRunning unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)running 1 test test tests::it_adds_two ... FAILEDfailures:---- tests::it_adds_two stdout ---- thread 'main' panicked at 'assertion failed: `(left == right)`left: `4`,right: `5`', src/lib.rs:11:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtracefailures:tests::it_adds_twotest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00serror: test failed, to rerun pass `--lib`测试捕获到了 bug!
it_adds_two测试失败,错误信息告诉我们断言失败了,它告诉我们assertion failed: `(left == right)`以及left和right的值是什么。这个错误信息有助于我们开始调试:它说assert_eq!的left参数是4,而right参数,也就是add_two(2)的结果,是5。可以想象当有很多测试在运行时这些信息是多么的有用。需要注意的是,在一些语言和测试框架中,断言两个值相等的函数的参数被称为
expected和actual,而且指定参数的顺序非常重要。然而在 Rust 中,它们则叫做left和right,同时指定期望的值和被测试代码产生的值的顺序并不重要。这个测试中的断言也可以写成assert_eq!(add_two(2), 4),这时失败信息仍同样是assertion failed: `(left == right)`。
assert_ne!宏在传递给它的两个值不相等时通过,而在相等时失败。在代码按预期运行,我们不确定值 会 是什么,不过能确定值绝对 不会 是什么的时候,这个宏最有用处。
assert_eq!和assert_ne!宏在底层分别使用了==和!=。当断言失败时,这些宏会使用调试格式打印出其参数,这意味着被比较的值必须实现了PartialEq和Debugtrait。所有的基本类型和大部分标准库类型都实现了这些 trait。对于自定义的结构体和枚举,需要实现PartialEq才能断言它们的值是否相等。需要实现Debug才能在断言失败时打印它们的值。因为这两个 trait 都是派生 trait。
🎯自定义错误信息
也可以向
assert!、assert_eq!和assert_ne!宏传递一个可选的失败信息参数,可以在测试失败时将自定义失败信息一同打印出来。任何在assert!的一个必需参数和assert_eq!和assert_ne!的两个必需参数之后指定的参数都会传递给format!宏,所以可以传递一个包含{}占位符的格式字符串和需要放入占位符的值。自定义信息有助于记录断言的意义;当测试失败时就能更好的理解代码出了什么问题。pub fn greeting(name: &str) -> String {format!("Hello {}!", name) }#[cfg(test)] mod tests {use super::*;#[test]fn greeting_contains_name() {let result = greeting("Carol");assert!(result.contains("Carol"));} }这个程序的需求还没有被确定,因此问候文本开头的
Hello文本很可能会改变。然而我们并不想在需求改变时不得不更新测试,所以相比检查greeting函数返回的确切值,我们将仅仅断言输出的文本中包含输入参数。让我们通过将
greeting改为不包含name在代码中引入一个 bug 来测试失败时是怎样的:pub fn greeting(name: &str) -> String {String::from("Hello!") }如果仅仅告诉了我们断言失败了和失败的行号。一个更有用的失败信息应该打印出
greeting函数的值。让我们为测试函数增加一个自定义失败信息参数:带占位符的格式字符串,以及greeting函数的值:#[test]fn greeting_contains_name() {let result = greeting("Carol");assert!(result.contains("Carol"),"Greeting did not contain name, value was `{}`",result);}
🎯使用should_panic检查恐慌
除了检查返回值之外,检查代码是否按照期望处理错误也是很重要的。
可以通过对函数增加另一个属性
should_panic来实现这些。这个属性在函数中的代码 panic 时会通过,而在其中的代码没有 panic 时失败。pub struct Guess {value: i32, }impl Guess {pub fn new(value: i32) -> Guess {if value < 1 || value > 100 {panic!("Guess value must be between 1 and 100, got {}.", value);}Guess { value }} }#[cfg(test)] mod tests {use super::*;#[test]#[should_panic]fn greater_than_100() {Guess::new(200);} }
🎃让should_panic更加精准
然而
should_panic测试结果可能会非常含糊不清。should_panic甚至在一些不是我们期望的原因而导致 panic 时也会通过。为了使should_panic测试结果更精确,我们可以给should_panic属性增加一个可选的expected参数。测试工具会确保错误信息中包含其提供的文本。// --snip--impl Guess {pub fn new(value: i32) -> Guess {if value < 1 {panic!("Guess value must be greater than or equal to 1, got {}.",value);} else if value > 100 {panic!("Guess value must be less than or equal to 100, got {}.",value);}Guess { value }} }#[cfg(test)] mod tests {use super::*;#[test]#[should_panic(expected = "less than or equal to 100")]fn greater_than_100() {Guess::new(200);} }这个测试会通过,因为
should_panic属性中expected参数提供的值是Guess::new函数 panic 信息的子串。我们可以指定期望的整个 panic 信息,在这个例子中是Guess value must be less than or equal to 100, got 200.。expected信息的选择取决于 panic 信息有多独特或动态,和你希望测试有多准确。在这个例子中,错误信息的子字符串足以确保函数在else if value > 100的情况下运行。
🎯在测试中使用Result<T,E>
目前为止,我们编写的测试在失败时都会 panic。我们也可以使用
Result<T, E>编写测试!这是一个延伸自示例 11-1 的测试,使用Result<T, E>重写,并在失败时返回Err而非 panic:#[cfg(test)] mod tests {#[test]fn it_works() -> Result<(), String> {if 2 + 2 == 4 {Ok(())} else {Err(String::from("two plus two does not equal four"))}} }现在
it_works函数的返回值类型为Result<(), String>。在函数体中,不同于调用assert_eq!宏,而是在测试通过时返回Ok(()),在测试失败时返回带有String的Err。不能对这些使用
Result<T, E>的测试使用#[should_panic]注解。为了断言一个操作返回Err成员,不要使用对Result<T, E>值使用问号表达式(?)。而是使用assert!(value.is_err())。
相关文章:
【Rust】——编写自动化测试(一)
🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL:…...
第十二章 微服务核心(一)
一、Spring Boot 1.1 SpringBoot 构建方式 1.1.1 通过官网自动生成 进入官网:https://spring.io/,点击 Projects --> Spring Framework; 拖动滚动条到中间位置,点击 Spring Initializr 或者直接通过 https://start.spring…...
MySQL索引18连问,谁能顶住
前言 过完这个节,就要进入金银季,准备了 18 道 MySQL 索引题,一定用得上。 作者:感谢每一个支持: github 1. 索引是什么 索引是一种数据结构,用来帮助提升查询和检索数据速度。可以理解为一本书的目录&…...
[flink 实时流基础系列]揭开flink的什么面纱基础一
Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。 文章目录 0. 处理无界和有界数据无界流有界流 1. Flink程序和数据流图2. 为什么一定要…...
开放平台 - 互动玩法演进之路
本期作者 1. 背景 随着直播业务和用户规模日益壮大,如何丰富直播间内容、增强直播间内用户互动效果,提升营收数据变得更加关键。为此,直播互动玩法应运而生。通过弹幕、礼物、点赞、大航海等方式,用户可以参与主播的直播内容。B站…...
Linux之进程控制进程终止进程等待进程的程序替换替换函数实现简易shell
文章目录 一、进程创建1.1 fork的使用 二、进程终止2.1 终止是在做什么?2.2 终止的3种情况&&退出码的理解2.3 进程常见退出方法 三、进程等待3.1 为什么要进行进程等待?3.2 取子进程退出信息status3.3 宏WIFEXITED和WEXITSTATUS(获取…...
RegSeg 学习笔记(待完善)
论文阅读 解决的问题 引用别的论文的内容 可以用 controlf 寻找想要的内容 PPM 空间金字塔池化改进 SPP / SPPF / SimSPPF / ASPP / RFB / SPPCSPC / SPPFCSPC / SPPELAN  ASPP STDC:short-term dense concatenate module 和 DDRNet SE-ResNeXt …...
Qt中常用宏定义
Qt中常用宏定义 一、Q_DECLARE_PRIVATE(Class)二、Q_DECLARE_PRIVATE_D(Dptr, Class)三、Q_DECLARE_PUBLIC(Class)四、Q_D(Class) 和 Q_Q(Class) 一、Q_DECLARE_PRIVATE(Class) #define Q_DECLARE_PRIVATE(Class) inline Class##Private* d_func() { # 此处的 d_ptr 是属于QOb…...
【计算机网络】第 9 问:四种信道划分介质访问控制?
目录 正文什么是信道划分介质访问控制?什么是多路复用技术?四种信道划分介质访问控制1. 频分多路复用 FDM2. 时分多路复用 TDM3. 波分多路复用 WDM4. 码分多路复用 CDM 正文 什么是信道划分介质访问控制? 信道划分介质访问控制(…...
Rust编程(五)终章:查漏补缺
闭包 & 迭代器 闭包(Closure)通常是指词法闭包,是一个持有外部环境变量的函数。外部环境是指闭包定义时所在的词法作用域。外部环境变量,在函数式编程范式中也被称为自由变量,是指并不是在闭包内定义的变量。将自…...
LLM漫谈(五)| 从q star视角解密OpenAI 2027年实现AGI计划
最近,网上疯传OpenAI2027年关于AGI的计划。在本文,我们将针对部分细节以第一人称进行分享。 摘要:OpenAI于2022年8月开始训练一个125万亿参数的多模态模型。第一个阶段是Arrakis,也叫Q*,该模型于2023年12月完成训练&…...
【echart】数据可视化+vue+vite遇到问题
1、vue3使用echars图表报错:"Initialize failed:invalid dom" 原因是因为:Dom没有完成加载时,echarts.init() 就已经开始执行了,获取不到Dom,无法进行操作 解决:加个延时 onMounted(async () …...
mac m1安装和使用nvm的问题
mac m1安装和使用nvm的问题 使用nvm管理多版本node 每个项目可能用的node版本不同,所以需要多个版本node来回切换 但是最近遇到安装v14.19.0时一直安装失败的问题。 首先说明一下,用的电脑是mac M1芯片 Downloading and installing node v14.19.0... …...
git泄露
git泄露 CTFHub技能树-Web-信息泄露-备份文件下载 当前大量开发人员使用git进行版本控制,对站点自动部署。如果配置不当,可能会将.git文件夹直接部署到线上环境。这就引起了git泄露漏洞。 工具GitHack使用:python2 GitHack.py URL地址/.git/ git命令…...
Java项目:78 springboot学生宿舍管理系统的设计与开发
作者主页:源码空间codegym 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统的角色:管理员、宿管、学生 管理员管理宿管员,管理学生,修改密码,维护个人信息。 宿管员…...
ArcGis Pro Python工具箱教程 03 工具箱中工具自定义
ArcGis Pro Python工具箱教程 03 工具箱中工具自定义 用于定义工作工具类的方法 工具方法必选或可选描述__ init __必需项right-aligned 初始化工具类。getParameterInfo可选定义工具的参数。isLicensed可选返回工具是否获得执行许可。updateParameters可选在用户每次在工具对…...
【C++初阶】之类和对象(中)
【C初阶】之类和对象(中) ✍ 类的六个默认成员函数✍ 构造函数🏄 为什么需要构造函数🏄 默认构造函数🏄 为什么编译器能自动调用默认构造函数🏄 自己写的构造函数🏄 构造函数的特性 ✍ 拷贝构造…...
Vue2(十一):脚手架配置代理、github案例、插槽
一、脚手架配置代理 1.回顾常用的ajax发送方式: (1)xhr 比较麻烦,不常用 (2)jQuery 核心是封装dom操作,所以也不常用 (3)axios 优势:体积小、是promis…...
在宝塔面板中,为自己的云服务器安装SSL证书,为所搭建的网站启用https(主要部分攻略)
前提条件 My HTTP website is running Nginx on Debian 10(或者11) 时间:2024-3-28 16:25:52 你的网站部署在Debain 10(或者11)的 Nginx上 安装单域名证书(默认)(非泛域名…...
学习JavaEE的日子 Day32 线程池
Day32 线程池 1.引入 一个线程完成一项任务所需时间为: 创建线程时间 - Time1线程中执行任务的时间 - Time2销毁线程时间 - Time3 2.为什么需要线程池(重要) 线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...
