当前位置: 首页 > news >正文

Rust之自动化测试(三): 测试组合

开发环境

  • Windows 10
  • Rust 1.73.0

 

  • VS Code 1.83.1

项目工程

这里继续沿用上次工程rust-demo

 测试组合

正如本章开始时提到的,测试是一个复杂的学科,不同的人使用不同的术语和组织。Rust社区根据两个主要类别来考虑测试:单元测试和集成测试。单元测试很小,也更集中,一次测试一个独立的模块,并且可以测试私有接口。集成测试完全在库的外部,使用代码的方式和其他外部代码一样,只使用公共接口,每次测试可能使用多个模块。

编写这两种类型的测试对于确保你的库的各个部分按照你期望的那样,单独地或者一起地工作是很重要的。

单元测试

单元测试的目的是独立于代码的其余部分测试每个代码单元,以快速查明代码在哪里以及没有按预期工作。您将把单元测试放在每个文件的src目录中,其中包含他们正在测试的代码。惯例是在每个文件中创建一个名为tests的模块来包含测试函数,并用cfg(test)来注释该模块。

测试模块和#[cfg(test)]

测试模块上的#[cfg(test)]注释告诉Rust只有在运行cargo test时才编译和运行测试代码,而不是在运行cargo build时。当您只想构建库时,这可以节省编译时间,并且因为不包括测试,所以可以节省最终编译工件的空间。您将会看到,因为集成测试位于不同的目录中,所以它们不需要#[cfg(test)]注释。但是,因为单元测试与代码在同一个文件中,所以您将使用#[cfg(test)]来指定它们不应该包含在编译结果中。 

回想一下,当我们在本章的第一节中生成新的adder项目时,Cargo为我们生成了以下代码: 

文件名:src/lib.rs

#[cfg(test)]
mod tests {#[test]fn it_works() {let result = 2 + 2;assert_eq!(result, 4);}
}

 这段代码是自动生成的测试模块。属性cfg代表配置,它告诉Rust只有在给定某个配置选项的情况下,才应该包含以下项目。在这种情况下,配置选项是test,它由Rust提供,用于编译和运行测试。通过使用cfg属性,只有当我们主动使用cargo test运行测试时,Cargo才会编译我们的测试代码。除了用#[test]注释的函数之外,这还包括可能在这个模块中的任何帮助函数。 

测试私有函数

在测试社区中有关于私有函数是否应该被直接测试的争论,其他语言使得私有函数很难或者不可能被测试。不管你坚持哪种测试思想,Rust的隐私规则确实允许你测试私有功能。考虑示例11-12中带有私有函数internal_adder的代码。

 文件名:src/lib.rs

pub fn add_two(a: i32) -> i32 {internal_adder(a, 2)
}fn internal_adder(a: i32, b: i32) -> i32 {a + b
}#[cfg(test)]
mod tests {use super::*;#[test]fn internal() {assert_eq!(4, internal_adder(2, 2));}
}

示例11-12:测试私有函数

注意,internal_adder函数没有标记为pub。测试只是Rust代码,tests模块只是另一个模块。正如我们在“模块树中引用项目的路径”一节中所讨论的,子模块中的项目可以使用它们的祖先模块中的项目。在这个测试中,我们use super::*tests模块的所有父项纳入范围,然后测试可以调用internal_adder。如果你认为私有函数不应该被测试,Rust中没有任何东西会强迫你这么做。

集成测试 

在Rust中,集成测试完全在你的库之外。他们使用你的库的方式和其他代码一样,这意味着他们只能调用属于你的库的公共API的函数。他们的目的是测试你的库的许多部分是否正确地一起工作。独立正常工作的代码单元在集成时可能会出现问题,因此集成代码的测试覆盖率也很重要。要创建集成测试,您首先需要一个测试目录。

tests目录

我们在项目目录的顶层创建一个测试目录,紧挨着src。Cargo知道在这个目录中寻找集成测试文件。然后,我们可以根据需要制作尽可能多的测试文件,Cargo会将每个文件编译成一个单独的crate箱。

让我们创建一个集成测试。示例11-12中的代码仍然在src/lib.rs文件中,创建一个tests目录,并创建一个名为tests/integration_test.rs的新文件。

adder
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    └── integration_test.rs

将示例11-13中的代码输入tests/integration_test.rs文件: 

文件名:tests/integration_test.rs

use adder;#[test]
fn it_adds_two() {assert_eq!(4, adder::add_two(2));
}

示例11-13:adder crate箱中函数的集成测试

tests目录中的每个文件都是一个单独的crate箱,所以我们需要将我们的库放到每个测试箱子的范围内。因此,我们在代码的顶部添加了use adder,这在单元测试中是不需要的。 

我们不需要用#[cfg(test)]注释tests/integration_test.rs中的任何代码。Cargo对tests目录进行了特殊处理,仅当我们运行cargo test时才编译该目录中的文件。立即运行cargo test

$ cargo testCompiling adder v0.1.0 (file:///projects/adder)Finished test [unoptimized + debuginfo] target(s) in 1.31sRunning unittests src/lib.rs (target/debug/deps/adder-1082c4b063a8fbe6)running 1 test
test tests::internal ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning tests/integration_test.rs (target/debug/deps/integration_test-1082c4b063a8fbe6)running 1 test
test it_adds_two ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sDoc-tests adderrunning 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 输出的三个部分包括单元测试、集成测试和doc测试。请注意,如果某个部分中的任何测试失败,下面的部分将不会运行。例如,如果一个单元测试失败,集成和文档测试将不会有任何输出,因为这些测试只有在所有单元测试都通过的情况下才会运行。

单元测试的第一部分和我们看到的一样:每个单元测试一行(我们在示例11-12中添加了一个名为internal的行),然后是单元测试的总结行。 

集成测试部分从Running tests/integration_test.rs行开始,接下来,集成测试中的每个测试函数都有一行,在Doc-tests adder部分开始之前,集成测试的结果有一个摘要行。 

每个集成测试文件都有自己的部分,所以如果我们在测试目录中添加更多的文件,就会有更多的集成测试部分。

我们仍然可以通过将测试函数的名称指定为cargo test的参数来运行特定的集成测试函数。要运行特定集成测试文件中的所有测试,请使用cargo test- test参数,后跟文件名:

$ cargo test --test integration_testCompiling adder v0.1.0 (file:///projects/adder)Finished test [unoptimized + debuginfo] target(s) in 0.64sRunning tests/integration_test.rs (target/debug/deps/integration_test-82e7799c1bc62298)running 1 test
test it_adds_two ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 此命令仅运行tests/integration_test.rs文件中的测试。

集成测试中的子模块

当您添加更多的集成测试时,您可能想要在测试目录中创建更多的文件来帮助组织它们;例如,您可以根据测试功能对测试功能进行分组。如前所述,tests目录中的每个文件都被编译成它自己的单独的箱,这对于创建单独的范围来更接近地模拟最终用户使用您的箱的方式是很有用的。然而,这意味着tests目录中的文件与src中的文件不具有相同的行为,正如你在第7章中了解到的如何将代码分成模块和文件。

当您有一组在多个集成测试文件中使用的帮助函数,并且您试图按照第7章“将模块分离到不同的文件”一节中的步骤将它们提取到一个公共模块中时,测试目录文件的不同行为是最明显的。例如,如果我们创建tests/common.rs并在其中放置一个名为setup的函数,我们可以向setup添加一些代码,我们希望从多个测试文件中的多个测试函数调用这些代码:

文件名:tests/common.rs

pub fn setup() {// setup code specific to your library's tests would go here
}

 当我们再次运行测试时,我们将在common.rs文件的测试输出中看到一个新的部分,尽管这个文件不包含任何测试函数,也没有从任何地方调用setup函数:

$ cargo testCompiling adder v0.1.0 (file:///projects/adder)Finished test [unoptimized + debuginfo] target(s) in 0.89sRunning unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)running 1 test
test tests::internal ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning tests/common.rs (target/debug/deps/common-92948b65e88960b4)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning tests/integration_test.rs (target/debug/deps/integration_test-92948b65e88960b4)running 1 test
test it_adds_two ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sDoc-tests adderrunning 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

common出现在测试结果中,并显示正在running 0 tests,这不是我们想要的。我们只是想与其他集成测试文件共享一些代码。 

 为了避免common出现在测试输出中,我们不创建tests/common.rs,而是创建tests/common/mod.rs。

├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    ├── common
    │   └── mod.rs
    └── integration_test.rs

 这是我们在之前的章节“替代文件路径”一节中提到的,Rust也理解的旧命名约定。以这种方式命名文件告诉Rust不要将common模块视为集成测试文件。当我们将setup函数代码移动到tests/common/mod.rs中并删除tests/common.rs文件时,测试输出中的部分将不再出现。测试目录子目录中的文件不会被编译成单独的箱子,也不会在测试输出中包含任何部分。

在我们创建了tests/common/mod.rs之后,我们可以从任何集成测试文件中将它作为一个模块来使用。下面是一个从tests/integration_test.rs中的it_adds_two测试调用setup函数的示例: 

文件名:tests/integration_test.rs 

use adder;mod common;#[test]
fn it_adds_two() {common::setup();assert_eq!(4, adder::add_two(2));
}

 注意,mod common;声明与我们在示例7-21中演示的模块声明相同。然后在测试函数中,我们可以调用common::setup()函数。

二元crate箱的集成测试 

如果我们的项目是一个只包含src/main.rs文件而没有src/lib.rs文件的二进制文件,我们就不能在tests目录中创建集成测试,也不能用use语句将src/main.rs文件中定义的函数带入范围。只有库crate箱暴露了其他箱可以使用的功能;二进制crate箱是用来独立运行的。

这就是提供二进制文件的Rust项目有一个直接的src/main.rs文件来调用src/lib.rs文件中的逻辑的原因之一。使用这种结构,集成测试可以测试库的use情况,以使重要的功能可用。如果重要的功能起作用,src/main.rs文件中的少量代码也将起作用,并且这少量代码不需要测试。 

总结 

Rust的测试功能提供了一种方式来指定代码应该如何运行,以确保它继续按照您的预期工作,即使您进行了更改。单元测试分别测试库的不同部分,并且可以测试私有的实现细节。集成测试检查库的许多部分是否能正确地协同工作,它们使用库的公共API来测试代码,就像外部代码使用它一样。尽管Rust的类型系统和所有权规则有助于防止某些类型的错误,但测试对于减少与代码预期行为有关的逻辑错误仍然很重要。

让我们结合你在本章和前几章学到的知识来做一个项目!

本章重点

  • 测试组合的概念和分类
  • 单元测试的概念和使用
  • 集成测试的概念和使用 

相关文章:

Rust之自动化测试(三): 测试组合

开发环境 Windows 10Rust 1.73.0 VS Code 1.83.1 项目工程 这里继续沿用上次工程rust-demo 测试组合 正如本章开始时提到的,测试是一个复杂的学科,不同的人使用不同的术语和组织。Rust社区根据两个主要类别来考虑测试:单元测试和集成测试。单元测试很…...

专业管理菜单的增删改、查重

1,点击专业管理菜单------查询所有专业信息列表 ①点击菜单,切换专业组件 ②切换到列表组件后,向后端发送请求到Servlet ③调用DAO层,查询数据库(sql),封装查询到的内容 ④从后端向前端做出…...

vue3插件开发,上传npm

创建插件 在vue3工程下,创建组件vue页: toolset.vue。并设置组件名称。注册全局组件。新建index.js文件。内容如下,可在main.js中引入index.js,注册该组件进行测试。![在这里插入图片描述](https://img-blog.csdnimg.cn/a3409d2cbeec41c797d5…...

python【多线程、单线程、异步编程】三个版本--在爬虫中的应用

并发编程在爬虫中的应用 之前的课程,我们已经为大家介绍了 Python 中的多线程、多进程和异步编程,通过这三种手段,我们可以实现并发或并行编程,这一方面可以加速代码的执行,另一方面也可以带来更好的用户体验。爬虫程…...

大模型LLM相关面试题整理-位置编码-tokenizer-激活函数-layernorm

10 LLMs 位置编码篇 10.1.1 什么是位置编码? 位置编码是一种用于在序列数据中为每个位置添加位置信息的技术。在自然语言处理中,位置编码通常用于处理文本序列。由于传统的神经网络无法直接捕捉输入序列中的位置信息,位置编码的引入可以帮助…...

python在nacos注册微服务

安装 首先需要安装python的nacos sdk pip install nacos-sdk-python 注册 注册过程非常简单,需要注意的是,注册完要定时发送心跳,否则服务会被nacos删掉。 import nacos import timeSERVER_ADDRESSES "http://1.2.3.4:8848" …...

tuxera ntfs2024破解版mac电脑磁盘读写软件

大家都知道由于操作系统的原因,在苹果电脑上不能够读写NTFS磁盘,但是,今天小编带来的这款tuxera ntfs 2024 mac版,完美的解决了这个问题。这是一款在macOS平台上使用的磁盘读写软件,能够实现苹果Mac OS X系统读写Micro…...

【源码】C++坦克大战源码

文章目录 题目介绍你收到的所有文件源码效果展示报告内容 题目介绍 代码量:1450 语言:C 你收到的所有文件 其中一个是devc版本,也可以用visual stdio 运行。 源码效果展示 typedef struct //这里的出现次序指的是一个AI_ta…...

AIO开放接口平台免费畅享ChatGPT聊天、联网互动、学术等服务!更有DALL·E 3最强AI绘图功能!

免费畅享! AIO平台ChatGPT联网、聊天、学术等服务! AIO开放接口平台 | 服务介绍 ALL IN ONE (AIO)API服务是LLM(大语言模型)开放接口平台:持续接入各种主流的大模型接口,并提供简单、易用、统一的API交互…...

【python】屈小原现在要为学校写校庆贺文(CTGU百年校庆)

题目: """ 题目描述: 屈小原需要为学校的校庆写一篇贺文,共需写下n个字,但他目前只完成了1个字。屈小原可以进行两种操作: 在文档的末尾添加一个字,这样字数就会变为x1。 写下与当前字数相同…...

探索未来的视觉革命:卷积神经网络的崭新时代(二)

💗💗💗欢迎来到我的博客,你将找到有关如何使用技术解决问题的文章,也会找到某个技术的学习路线。无论你是何种职业,我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章,也欢…...

博客后台模块续更(三)

四、后台模块-动态路由 实现了这个动态路由功能之后,就能在浏览器web页面登录进博客管理后台了 1. 接口分析 后台系统需要能实现不同的用户权限可以看到不同的功能,即左侧的导航栏 请求方式 请求地址 请求头 GET /getRouters 需要token请求头 …...

第十二届蓝桥杯模拟赛第三期

A填空题 问题描述 请问在 1 到 2020 中&#xff0c;有多少个数与 2020 互质&#xff0c;即有多少个数与 2020 的最大公约数为 1。 参考答案 800 public class Main {public static void main(String[] args) {int ans0;for(int i1;i<2020;i) {if(gcd(2020,i)1) {ans;}}…...

2023年浙大MEM考前80天上岸经验分享

时间过得真快&#xff0c;转眼间已经是十月份了。回想起去年这个时候&#xff0c;我还在为考研而感到焦虑不安。然而&#xff0c;如今我已经在浙大MEM项目学习了一个多月的时间了。在这一个月的学习过程中&#xff0c;我不仅学到了许多专业知识&#xff0c;还结识了很多志同道合…...

增加并行度后,发现Flink窗口不会计算的问题。

文章目录 前言一、现象二、结论三、解决 前言 窗口没有关闭计算的问题&#xff0c;一直困扰了很久&#xff0c;经过多次验证&#xff0c;确定了问题的根源。 一、现象 Flink使用了window&#xff0c;同时使用了watermark &#xff0c;并且还设置了较高的并行度。生产是设置了…...

使用 JMeter 和 Docker 进行服务存根

用于性能测试的服务存根&#xff1a;简介 随着测试项目的复杂性不断增加&#xff0c;越来越多的被测系统的测试流程受到依赖系统的影响。当我说“依赖系统”时&#xff0c;我指的是&#xff1a; 不受当前开发影响的遗留系统 属于另一个组织的第三方服务 您的组织开发的系统&am…...

《王道计算机考研——操作系统》学习笔记总目录+思维导图

本篇文章是对《王道计算机考研——操作系统》所有知识点的笔记总结归档和计算机网络的思维导图 学习视频&#xff1a;王道计算机考研 操作系统 408四件套【计网、计组、操作系统、数据结构】完整课堂PPT 思维导图 &#xff08;求Star~&#xff09;&#xff1a;【王道考研】计…...

多模态及图像安全的探索与思考

前言 第六届中国模式识别与计算机视觉大会&#xff08;The 6th Chinese Conference on Pattern Recognition and Computer Vision, PRCV 2023&#xff09;已于近期在厦门成功举办。通过参加本次会议&#xff0c;使我有机会接触到许多来自国内外的模式识别和计算机视觉领域的研究…...

基础算法相关笔记

排序 最好情况下&#xff1a; 冒泡排序 最坏时间复杂度 O ( n 2 ) O(n^2) O(n2)。 插入排序 最坏时间复杂度为 O ( n 2 ) O(n^2) O(n2)&#xff0c;最优时间复杂度为 O ( n ) O(n) O(n)。 平均情况下&#xff1a; 快速排序 最坏时间复杂度为 O ( n 2 ) O(n^2) O(n2)&…...

Mac电脑无法识别移动硬盘怎么办?

很多人都喜欢在Mac电脑上办公、学习&#xff0c;但有时我们将移动硬盘连接Mac电脑时&#xff0c;却会发现电脑无法识别移动硬盘。那么&#xff0c;Mac电脑无法识别移动硬盘怎么办呢&#xff1f; Mac无法识别移动硬盘的原因 导致Mac不识别移动硬盘的原因有很多&#xff0c;你可…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...