【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 中的测试函数是用来验证非测试代码是否是按照期望的方式运行的。测试函数体通常执行如下三种操作:

  1. 设置任何所需的数据或状态
  2. 运行需要测试的代码
  3. 断言其结果是我们所期望的

🎃解剖测试函数

        测试成功:

        Rust 中的测试就是一个带有 test 属性注解的函数。属性(attribute)是关于 Rust 代码片段的元数据;第五章中结构体中用到的 derive 属性就是一个例子。为了将一个函数变成测试函数,需要在 fn 行之前加上 #[test]。当使用 cargo test 命令运行测试时,Rust 会构建一个测试执行程序用来调用被标注的函数,并报告每一个测试是通过还是失败。

        每次使用 Cargo 新建一个库项目时,它会自动为我们生成一个测试模块和一个测试函数。这个模块提供了一个编写测试的模板,为此每次开始新项目时不必去查找测试函数的具体结构和语法了。因为这样当然你也可以额外增加任意多的测试函数以及测试模块。

        实际编写测试代码之前,让我们先通过尝试那些自动生成的测试模版来探索测试是如何工作的。接着,我们会写一些真正的测试,调用我们编写的代码并断言它们的行为的正确性。

$ cargo new adder --lib
     Created 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! 宏提供一个求值为布尔值的参数。如果值是 trueassert! 什么也不做,同时测试会通过。如果值为 falseassert! 调用 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 test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- 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 backtrace


failures:
    tests::it_adds_two

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

error: 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())

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PowerBI加权计算权重

1.打开主页&#xff0c;点击快速度量值 2.计算里面 选择计算&#xff1a;每个类别的加权平均值 3.就是添加数据&#xff0c;基值&#xff08;就是你要计算的值&#xff09;粗细&#xff08;就是你要用那个值计算权重&#xff09;类别&#xff08;就是你是要乘以那个类别&#x…

C语言数据结构基础——排序

目录 1.插入排序 2.冒泡排序 3. 堆排序 4.希尔排序 5.直接选择排序 6.快速排序☆☆ 6.1快速排序基础 6.2关于快速排序的时间复杂度 6.3随机数法和三数取中法 6.4其他的单趟实现方法 6.4.1挖坑法 6.4.2前后指针版快速排序☆ 6.4.3非递归实现快排☆ 7.归并排序 7.1递归…

|行业洞察·碳纤维|《中国碳纤维行业现状与发展趋势-39页》

报告内容的详细解读&#xff1a; 1. 战略性新材料的重要性 碳纤维是一种轻质高强的高性能纤维材料&#xff0c;在航空航天、国防军工、高端装备制造等领域具有不可替代的作用。碳纤维的应用有助于减少能源消耗和降低碳排放&#xff0c;符合全球可持续发展的要求。 |趋势洞察…

2024/03/28(C++·day4)

一、思维导图 二、练习题 1、写出三种构造函数&#xff0c;算术运算符、关系运算符、逻辑运算符重载尝试实现自增、自减运算符的重载 #include <iostream>using namespace std;// 构造函数示例 class MyClass { private:int data; public:// 默认构造函数MyClass() {da…

【3DsMax+Pt】练习案例

目录 一、在3DsMax中展UV 二、在Substance 3D Painter中绘制贴图 一、在3DsMax中展UV 1. 首先创建如下模型 2. 选中如下三条边线作为接缝 重置剥 发现如下部分还没有展开 再选一条边作为接缝 再次拨开 拨开后的UV如下 二、在Substance 3D Painter中绘制贴图 1. 新建项目&am…

Java Swing游戏开发学习20

内容来自RyiSnow视频讲解 这一节讲的是Monster野兽、就是常说的游戏中的怪&#xff0c;打怪升级的那个怪。 前言 本节目标 实现怪处理碰撞和伤害&#xff08;当玩家player碰到怪会掉血&#xff09; 实现 添加怪到窗口 这里只使用了2张图片&#xff0c;每个方向移动都是用…

C语言用if语句设计选择结构程序

在C语言中&#xff0c;if语句是一种常用的选择结构语句&#xff0c;用于根据条件选择性地执行不同的代码块。if语句的设计使得程序可以根据条件的真假进行分支控制&#xff0c;从而实现灵活的程序逻辑。本文将深入介绍C语言中如何使用if语句设计选择结构程序&#xff0c;包括if…

激光焊接机在不锈钢三角阀制造中的应用与发展

不锈钢三角阀激光焊接机是一种专门用于焊接不锈钢三角阀的高效、精准设备。这种设备在不锈钢三角阀的制造过程中起到了至关重要的作用&#xff0c;其应用主要体现在以下几个方面&#xff1a; ​ 一、激光焊接机在不锈钢三角阀制造中的应用 激光焊接机以其独特的优势&#xff…

金属板材成型仿真软件 Altair® Inspire™ Form,完整的冲压仿真环境

Inspire Form 是一个完整的冲压仿真环境&#xff0c;产品设计师和工艺工程师可以使用该环境&#xff0c;有效地优化设计、对稳健的制造进行仿真、降低材料成本。 借助快速简便的可行性模块&#xff0c;用户可以在几秒钟内完成零部件分析&#xff0c;从而在产品开发早期阶段预测…

李宏毅【生成式AI导论 2024】第6讲 大型语言模型修炼_第一阶段_ 自我学习累积实力

背景知识:机器怎么学会做文字接龙 详见:https://blog.csdn.net/qq_26557761/article/details/136986922?spm=1001.2014.3001.5501 在语言模型的修炼中,我们需要训练资料来找出数十亿个未知参数,这个过程叫做训练或学习。找到参数后,我们可以使用函数来进行文字接龙,拿…

【Pt】马灯贴图绘制过程 02-制作锈迹

目录 一、边缘磨损效果 二、刮痕效果 三、边缘磨损与刮痕的混合 四、锈迹效果 本篇效果&#xff1a; 一、边缘磨损效果 将智能材质“Iron Forge Old” 拖入图层 打开“Iron Forge Old” 文件夹&#xff0c;选中“Sharpen”&#xff08;锐化&#xff09;&#xff0c;增大“…

fpga 通过axi master读写PS侧DDR的仿真和上板测试

FPGA和ARM数据交互是ZYNQ系统中非常重要的内容。PS提供了供FPGA读写的AXI-HP接口用于两者的高速通信和数据交互。一般的&#xff0c;我们会采用AXI DMA的方式去传输数据&#xff0c;DMA代码基本是是C编写&#xff0c;对于FPGA开发者来说不利于维护和debug。本文提供一种手写AXI…

《思考,快与慢》揭示了决策过程中直觉反应与理性分析的关系 - 三余书屋 3ysw.net

思考&#xff0c;快与慢 你好&#xff0c;今天我们要分享的是《思考&#xff0c;快与慢》。作者是丹尼尔卡尼曼&#xff0c;2002年诺贝尔经济学奖获得者。他开辟了经济学中的一个新分支——行为经济学。在《思考&#xff0c;快与慢》这部作品中&#xff0c;他深入探讨了行为经…

JVM篇详细分析

JVM总体图 程序计数器&#xff1a; 线程私有的&#xff0c;每个线程一份&#xff0c;内部保存字节码的行号&#xff0c;用于记录正在执行字节码指令的地址。&#xff08;可通过javap -v XX.class命令查看&#xff09; java堆&#xff1a; 线程共享的区域&#xff0c;用来保存对…

搭建企业微信知识库,这些注意事项你必须知道

| 企业微信知识库是什么&#xff1f; 简单来说&#xff0c;企业微信知识库就是一个集中存储、管理和分享企业内部信息的置于企业微信中的系统。你可以把它想象成一个超级大的“资料库”&#xff0c;里面装满了公司的各种知识、文档、流程、经验等等。这个“资料库”不仅方便员工…

劳保鞋厂家与您聊聊:从事电力行业工作人员穿什么功能的劳保鞋

电力行业属于危险系数较高的行业&#xff0c;工作人员在工作中面临电力的潜在危险&#xff0c;如电击、高温、机械伤害、高空作业等风险。这就要有专业的安全设备&#xff0c;才能尽可能的保护电力工作人员的安全&#xff0c;真真正正起到防范的作用。因此&#xff0c;穿着合适…

学习或复习电路的game推荐:nandgame(NAND与非门游戏)、Turing_Complete(图灵完备)、logisim工具

https://www.nandgame.com/ 免费 https://store.steampowered.com/app/1444480/Turing_Complete/ 收费&#xff0c;70元。据说可以导出 Verilog &#xff01; logisim及其衍生版本 都需要安装java环境。 http://www.cburch.com/logisim/ 是原版&#xff0c; 下载页面&#…

Java的静态代理与jdk动态代理

代理 我们经常利用代理进行解耦以及控制对实际对象的访问等工作。例如&#xff0c;我们可以通过代理对方法的调用进行更精细的控制&#xff08;例如加上日志、权限控制等&#xff09;&#xff0c;而无需修改实际对象的代码。代理的作用是无侵入式的给代码增加功能。有些事情是…

【分布式】——CAPBASE理论

CAP&BASE理论 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/tree-learning-notes ⭐⭐⭐⭐⭐⭐ Spring专栏&#x1f449;https://blog.csdn.net/weixin_53580595/category_12279588.html Sprin…

物联网实战--入门篇之(一)物联网概述

目录 一、前言 二、知识梳理 三、项目体验 四、项目分解 一、前言 近几年很多学校开设了物联网专业&#xff0c;但是确却地讲&#xff0c;物联网属于一个领域&#xff0c;包含了很多的专业或者说技能树&#xff0c;例如计算机、电子设计、传感器、单片机、网…