【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
和Debug
trait。所有的基本类型和大部分标准库类型都实现了这些 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…...
@Transactional 注解使用的注意事项
事务管理 事务管理在系统开发中是不可缺少的一部分,Spring提供了很好的事务管理机制,主要分为编程式事务和声明式事务两种。 编程式事务: 是指在代码中手动的管理事务的提交、回滚等操作,代码侵入比较强。 声明式事务ÿ…...
电商系列之库存
> 插:AI时代,程序员或多或少要了解些人工智能,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 坚持不懈,越努力越幸运,大家…...
Apache HBase(二)
目录 一、Apache HBase 1、HBase Shell操作 1.1、DDL创建修改表格 1、创建命名空间和表格 2、查看表格 3、修改表 4、删除表 1.2、DML写入读取数据 1、写入数据 2、读取数据 3、删除数据 2、大数据软件启动 一、Apache HBase 1、HBase Shell操作 先启动HBase。再…...
【设计模式】原型模式详解
概述 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象 结构 抽象原型类:规定了具体原型对象必须实现的clone()方法具体原型类:实现抽象原型类的clone()方法,它是可以被复制的对象。访问类&…...
企微侧边栏开发(内部应用内嵌H5)
一、背景 公司的业务需要用企业微信和客户进行沟通,而客户的个人信息基本都存储在内部CRM系统中,对于销售来说需要一边看企微,一边去内部CRM系统查询,比较麻烦,希望能在企微增加一个侧边栏展示客户的详细信息…...
如何确定最优的石油管道位置
如何确定最优的石油管道位置 一、前言二、问题概述三、理解问题的几何性质四、转化为数学问题五、寻找最优解六、算法设计6.1伪代码6.2 C代码七算法效率和实际应用7.1时间效率分析7.2 空间效率分析结论一、前言 当我们面对建设大型输油管道的复杂任务时,确保效率和成本效益是…...
FPGA 图像边缘检测(Canny算子)
1 顶层代码 timescale 1ns / 1ps //边缘检测二阶微分算子:canny算子module image_canny_edge_detect (input clk,input reset, //复位高电平有效input [10:0] img_width,input [ 9:0] img_height,input [ 7:0] low_threshold,input [ 7:0] high_threshold,input va…...
2024.3.28学习笔记
今日学习韩顺平java0200_韩顺平Java_对象机制练习_哔哩哔哩_bilibili 今日学习p286-p294 继承 继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些…...
33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查
33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查 关系数据库 关系对象数据库(ORM) 应用偏好数据库 分布式数据库 关系型数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。HarmonyOS关系型…...
寄主机显示器被快递搞坏了怎么办?怎么破?
大家好,我是平泽裕也。 最近,我在社区里看到很多关于开学后弟弟寄来的电脑显示器被快递损坏的帖子。 看到它真的让我感到难过。 如果有人的数码产品被快递损坏了,我会伤心很久。 那么今天就跟大家聊聊寄快递的一些小技巧。 作为一名曾经的…...
哪些网站做外贸效果好/百度推广工作怎么样
SQL SERVER的数据类型 1.SQL SERVER的数据类型 数据类型是数据的一种属性,表示数据所表示信息的类型。任何一种计算机语言都定义了自己的数据类型。当然,不同的程序语言都具有不同的特点,所定义的数据类型的各类和名称都或多或少有些不同。SQ…...
律师网站建设/网站推广公司黄页
2004年2月28日,在浙江大学软件学院和CSDN网站的大力支持下,ERPTAO组织在浙大成功地举办了第一次软件技术讲座。有上百名专业软件开发者及爱好者到场参加,两位主讲人熊节(也就是我本人)和石一楹为大家送上了关于重构思想…...
2014 湖南个人网站备案可以做b2b吗/百度登陆页面
一个窗口有一个调节钮,可以用来控制另一个窗口的字体大小。关闭任意一个窗口,两个窗口同时关闭。# -*- coding: UTF-8 -*-from Tkinter import * #引入模块def resize(ev None):label.config(fontHelvetica -%d bold % scale.get()) #改变字体大小def c…...
电脑网站建设/郑州网站seo外包公司
///题目的大致意思是给你一个大写字母和下滑线组成的字符串,先计算出字符串在内存中实际占用的字节数,每个char占用8个字节 ///然后通过huffman编码后字符串所占的字节数,两者相除即可 #include <cstdio> #include <cstring>#in…...
网站动效是代码做的吗/上海网络seo公司
文章来源:深入理解多线程(二)—— Java的对象模型原文作者:Hollis来源平台:微信公众号上一篇文章中简单介绍过synchronized关键字的方式,其中,同步代码块使用monitorenter和monitorexit两个指令…...
wordpress看不到图片/郑州厉害的seo优化顾问
首先运用的是pandas数据分析模块和matplotlib数据绘图模块下面简单处理和操作import pandas as pd #使用pandas读取数据import matplotlib.pyplot as pl#导入图像库url"D:python数据挖掘图书配套数据、代码chapter3demodatacatering_sale.xls"#需要读取的数据路径dat…...