当前位置: 首页 > 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;你可…...

LVDS的几个关键电压概念

LVDS的几个关键电压概念 1.LVDS的直流偏置 直流偏置指的是信号的电压围绕的基准电压&#xff0c;信号的中心电压。在LVDS中&#xff0c;信号是差分的&#xff0c; 两根线之间的电压差表示数据&#xff0c;很多时候两根线的电压不是在0v开始变化的&#xff0c;而是在某个 固定的…...

复制与图片文件同名的标签文件到目标路径

引言&#xff1a;在数据集构建中&#xff0c;我们经常需要挑选一些特殊类型的图片&#xff08;如&#xff1a;零件中有特殊脏污背景的图片&#xff0c;写论文的时候想单独对这类情况进行热力图验证&#xff09;。我们把挑选出来的图片放到一个文件夹下&#xff0c;这时候我想快…...

产品笔试专业名词梳理

目录 产品常识 四种常见广告形式 贴片广告 中插广告 信息流广告 横幅广告 BAT和TMD BAT TMD 付费渗透率 蓝海市场、红海市场 蓝海市场 红海市场 竞品研究 SWOT分析 SWOT分析的核心目的&#xff1a; SWOT分析的优点&#xff1a; SWOT分析的局限与注意事项&…...

容器安全最佳实践:云原生环境下的零信任架构实施

&#x1f4cb; 目录 引言&#xff1a;容器安全的重要性零信任架构基础理论云原生环境的安全挑战容器安全威胁模型分析零信任架构在容器环境中的实施关键技术组件与工具安全策略与最佳实践监控与响应机制案例研究与实施路径未来发展趋势 引言 随着容器技术和云原生架构的快速…...

分类预测 | Matlab实现CNN-BiLSTM-Attention高光谱数据分类预测

分类预测 | Matlab实现CNN-BiLSTM-Attention高光谱数据分类预测 目录 分类预测 | Matlab实现CNN-BiLSTM-Attention高光谱数据分类预测分类效果功能概述程序设计参考资料 分类效果 功能概述 该MATLAB代码实现了一个结合CNN、BiLSTM和注意力机制的高光谱数据分类预测模型&#x…...

数据库系统概论(十七)超详细讲解数据库规范化与五大范式(从函数依赖到多值依赖,再到五大范式,附带例题,表格,知识图谱对比带你一步步掌握)

数据库系统概论&#xff08;十七&#xff09;超详细讲解数据库规范化与五大范式&#xff08;从函数依赖到多值依赖&#xff0c;再到五大范式&#xff0c;附带例题&#xff0c;表格&#xff0c;知识图谱对比带你一步步掌握&#xff09; 前言一、为什么需要规范化1. 我们先想一个…...

为什么 uni-app 开发的 App 没有明显出现屏幕适配问题Flutter 开发的 App 出现了屏幕适配问题

&#x1f9e9; 一、为什么 uni-app 开发的 App 没有明显出现屏幕适配问题&#xff1f; ✅ 1. uni-app 是基于 H5 的运行环境&#xff08;或类 H5&#xff09; uni-app 默认使用的是 H5 的渲染引擎&#xff08;如 WebView 或小程序渲染引擎&#xff09;。在 H5 中&#xff0c;…...

第2章:Neo4j安装与配置

在了解了Neo4j的基本概念和优势之后&#xff0c;下一步就是将其安装并配置好&#xff0c;以便开始实际操作。本章将详细介绍Neo4j的各种部署方式&#xff0c;涵盖不同操作系统的安装步骤&#xff0c;深入探讨关键配置项&#xff0c;并介绍常用的管理工具&#xff0c;为读者顺利…...

NTT印地赛车:数字孪生技术重构赛事体验范式,驱动观众参与度革命

引言&#xff1a;数字孪生技术赋能体育赛事&#xff0c;开启沉浸式观赛新纪元 在传统体育赛事观赛模式遭遇体验天花板之际&#xff0c;NTT与印地赛车系列赛&#xff08;NTT INDYCAR SERIES&#xff09;的深度合作&#xff0c;通过数字孪生&#xff08;Digital Twin&#xff09…...

Spring Boot实现接口时间戳鉴权

Spring Boot实现接口时间戳鉴权&#xff0c;签名&#xff08;sign&#xff09;和时间戳&#xff08;ts&#xff09;放入请求头&#xff08;Header&#xff09;。 一、请求头参数设计 参数名类型说明tsLong13位时间戳&#xff08;Unix毫秒值&#xff09;&#xff0c;必填&…...