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

news2024/12/28 5:14:58

开发环境

  • 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 test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 1.31s
     Running unittests src/lib.rs (target/debug/deps/adder-1082c4b063a8fbe6)

running 1 test
test tests::internal ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/integration_test.rs (target/debug/deps/integration_test-1082c4b063a8fbe6)

running 1 test
test it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test 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_test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.64s
     Running tests/integration_test.rs (target/debug/deps/integration_test-82e7799c1bc62298)

running 1 test
test it_adds_two ... ok

test 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 test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.89s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::internal ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/common.rs (target/debug/deps/common-92948b65e88960b4)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/integration_test.rs (target/debug/deps/integration_test-92948b65e88960b4)

running 1 test
test it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test 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的类型系统和所有权规则有助于防止某些类型的错误,但测试对于减少与代码预期行为有关的逻辑错误仍然很重要。

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

本章重点

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1120696.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java爬虫】使用selenium获取某交易所公司半年报数据

引言 上市公司的财报数据一般都会进行公开,我们可以在某交易所的官方网站上查看这些数据,由于数据很多,如果只是手动收集的话可能会比较耗时耗力,我们可以采用爬虫的方法进行数据的获取。 本文就介绍采用selenium框架进行公司财…

服务器通过scp传送数据,提示验证失败的问题

场景提示如下 当使用scp传送数据时提示这个 分析: 目标服务器云盘被格式化过, 用之前的密钥校验新的系统时发现不匹配了,拒绝登录! 解决方法 需要把旧密钥换成新的密钥 先看源服务器已经有的密钥ssh-keygen -l -f ~/.ssh/known_hosts然后重新生成一下…

简道云出现问题及解决方法1

1、在制作仪表盘设计的时候没有统计表链接,点开统计表没有显示。 根据老师的手把手教导还是会出现错误,上网查询再加上多次看录播回放,私以为是不同网页版本的问题,包括一些应用的排版同样不一样。这里的解决办法是把刚才做的表盘…

MySQL binlog集市的项目小结

这是学习笔记的第 2478篇文章 MySQL binlog集市的事情我们做了有一段时间了,最开始的初衷是异常操作的数据恢复,主要的痛点是如果发生了业务误操作,需要紧急恢复数据的时候,通常这些误操作是对于字典配置数据的变更,而…

有哪些适用于 Windows 的PDF 阅读器?免费 PDF 阅读器清单

探索适用于 Windows 10 和 11 的最佳 PDF 阅读器 适用于 Windows 10 和 Windows 11 的最佳 PDF 阅读器让您可以在台式计算机上查看和共享文档。 最好的PDF 编辑器和免费的 PDF 编辑器配备了先进的工具,可以跨不同的操作系统工作。但是,当您只需要查看和…

Docker搭建Plex流媒体服务并播放自己本地视频

Docker搭建Plex流媒体服务 安装Docker创建存储配置文件的目录创建Plex容器配置Plex设置媒体库访问Plex 1 介绍 Plex是一个流媒体服务器,可以轻松地将你的媒体文件库(如电影、电视节目和音乐)通过网络流式传输到各种设备上。 Plex 是一套媒体…

信号量、互斥锁、计数信号量

大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家来访。 信号量(semaphores)一个多进程共享的非负整型全局变量。信号量常用于多进程的进程同步。 介绍 …

零基础学python:错误与异常

嗨喽,大家好呀~这里是爱看美女的茜茜呐 语法错误 异常:大多数的异常都不会被程序处理,都以错误信息的形式展现在这里 👇 👇 👇 更多精彩机密、教程,尽在下方,赶紧点击了解吧~ pyth…

【STM32】GPIO控制LED(寄存器版)

在开始之前记得先准备好环境: STM32F103核心板下载教程.pdf 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com) 一、STM32的GPIO模块数据手册详解 每个GPIO端口对应16个引脚,例GPIOA(PA0~PA15)内核cpu就可以通过APB2总线对寄存器…

电路电子技术3 电位的计算受控源在电路分析中的作用

1.计算电压 思路:注意到这个串联电路,有两个电压源,所以我们首先可以将两个电压源串联等效为一个电压源。 即: 故可得答案选B. 2.计算点位 思路:题目说明了B点开路,所以没有流过电阻R的电流,…

光学配件简介

光学配件简介 延长管介绍 延长管,是加在镜头和相机之间的一根通心的环状管,但里面没有任何光学部件,也没有放大作用。它不会改变光圈值,但因为延长了镜头,光线会减弱。延长管可以使镜头的对焦距离缩短,也就…

Uniapp 酷炫钱包页面模板 直接引用

使用教程 直接引用Vue页面 即可 <template><view><TCqianbao></TCqianbao></view> </template> <script>import TCqianbao from /uni_modules/TC-qianbao/pages/index.vueexport default {components:{TCqianbao},} </script&…

【斗破年番】彩鳞送老公新挂,薰儿霸气回归,萧炎招揽古河,邀请美杜莎战魂殿

【侵权联系删除】【文/郑尔巴金】 斗破苍穹年番动画已经更新了&#xff0c;萧炎与美杜莎女王一起回娘家&#xff0c;并且也与小医仙制定了同盟计划&#xff0c;准备对金雁宗与慕兰谷斩草除根。从官方公布的第69集预告来看&#xff0c;慕兰三老与雁落天已经陨落&#xff0c;美杜…

Warning: [antd: Switch] `value` is not a valid prop, do you mean `checked`?

解决方案&#xff1a; <Form.Item label"更多设置" name"moreSetting" valuePropName"checked"><Switch onChange{handleMoreSetting} /></Form.Item> 参考链接&#xff1a; https://chat.xutongbao.top/

医药保健品经营小程序商城的作用是什么

互联网经济下&#xff0c;线上线下商家难以生存&#xff0c;自营平台成为各行业商家的选择&#xff0c;摆脱平台限制及线下门店地域的限制&#xff0c;多渠道线上获客引流转化营销、留存复购裂变等&#xff0c;更利于直接触达消费者&#xff0c;无其它商家夺流及坑位费、流量费…

最详细STM32,cubeMX 定时器

这篇文章将详细介绍 STM32,cubeMX 定时器的配置和使用。 文章目录 前言一、定时器基础知识二、cubeMX 配置三、定时时长四、自动生成代码讲解五、实验程序总结 前言 实验开发板&#xff1a;STM32F103C8T6。所需软件&#xff1a;keil5 &#xff0c; cubeMX 。实验目的&#xff…

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

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

自主武器系统对伦理和法律的挑战

【第十届北京香山论坛是中国军事科学学会、中国国际战略学会联合主办的论坛&#xff0c;将于2023年10月29日至31日在北京举行。本次论坛的主题是“共同安全、持久和平”&#xff0c;聚焦全球安全倡议重点合作方向&#xff0c;为各方共议安全难题、求解安全之策、促进安全合作提…

Leetcode1971. 寻找图中是否存在路径

Every day a Leetcode 题目来源&#xff1a;1971. 寻找图中是否存在路径 解法1&#xff1a;并查集 并查集介绍&#xff1a;并查集详解 代码&#xff1a; /** lc appleetcode.cn id1971 langcpp** [1971] 寻找图中是否存在路径*/// lc codestart class UnionFind {vector&…

Node-RED系列教程-27node-red操作邮件节点

提前注册好一个163邮箱: 安装以下节点: 演示发送邮件的功能