14.编写自动化测试(上)

news2024/11/24 7:29:18

标题

  • 一、如何编写测试
    • 1.1 一些概念
    • 1.2 测试函数剖析
    • 1.3 使用assert!宏检查结果
    • 1.4 使用assert_eq!和assert_ne!宏来测试相等
      • 1) assert_eq!
      • 2) assert_ne!
    • 1.5 使用 should_panic 检查 panic
  • 二、将 Result<T, E> 用于测试

一、如何编写测试

1.1 一些概念

Rust 中的测试函数体通常执行如下三种操作:

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

Rust提供的专门用来编写测试的功能:

  • test属性;
  • 一些宏;
  • should_panic属性;

1.2 测试函数剖析

  • Rust中的测试就是一个在fn行之间加上#[test]属性标注的函数;
  • cargo test命令执行时,会运行被标记了#[test]的函数,并报告是否运行通过;

创建新的库项目

cargo new adder --lib
  • 源码只有src/lib.rs文件,其内容如下
pub fn add(left: usize, right: usize) -> usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}
  • fn函数之前的#[test]表明这是一个测试函数;
  • tests是一个普通的内部模块,要测试外部的中的add函数,因此要用use引入;
  • 函数体通过使用assert_eq!宏来断言2加2等于4,这是测试用例的典型格式;
  • 使用cargo test运行测试用例;
    在这里插入图片描述
  • 结果显示:1 passed; 0 failed;…… 说明所有测试都通过了;
  • tests::it_works中的it_works是测试函数的名字;
  • ignored表示忽略的测试用例,filtered out表示过滤需要运行的测试,measured是针对性能测试的(目前只能用于Rust开发版(nightly Rust,详阅https://doc.rust-lang.org/unstable-book/library-features/test.html);
  • Doc-tests adder 是所有文档测试的结果;

再加一个失败的测试项

#[cfg(test)]
mod tests {
	……
	
    #[test]
    fn another() {
        panic!("Make this test fail");
    }
}

运行时会提示it_works运行成功而another运行失败
在这里插入图片描述

1.3 使用assert!宏检查结果

  • assert!宏由标准库提供,在希望确保测试中一些条件为true时非常有用;
  • 需要向assert!宏提供一个求值为布尔值的参数;
  • 如果值是true,则什么也不做,同时测试通过;
  • 如果值是false,assert!宏调用panic!宏,同时测试失败;
  • assert!最后附上错误提示字符串,以便更好定位 (可选)

为前面写的Rectangle结构体和一个can_hold添加一个测试用例,看看8*7的实例能否放下5*1的实例;

#[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
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle { width: 8, height: 7 };
        let smaller = Rectangle { width: 5, height: 1 };

        assert!(larger.can_hold(&smaller), "运行失败!");
    }
}

测试结果为通过
在这里插入图片描述

1.4 使用assert_eq!和assert_ne!宏来测试相等

  • 标准库提供assert_eq!assert_ne!宏比较相等或不相等;
  • 当断言失败时也会打印出这两个值具体是什么,以便于观察测试为什么失败;
  • 依然可以自定义断言失败时候的输出信息;
  • 宏参数中的左右两个值的顺序并不重要;

1) assert_eq!

fn add_two(a: i32) -> i32{
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        let result = add_two(2);
        assert_eq!(4, result);
    }

    #[test]
    fn it_adds_two2() {
        let result = add_two(1);
        assert_eq!(4, result, "4 is not equal {}.", result);
    }
}

输出结果
在这里插入图片描述

  • it_add_two运行通过;
  • it_adds_two2运行失败,左值为4右值为3,因此不相等,断言失败;
  • 输出了我们自己定义的错误提示信息:4 is not equal 3

2) assert_ne!

  • assert_ne!宏在传递给它的两个值不相等时通过;
  • 在代码按预期运行,我们不确定值会是什么,不过能确定值绝对不会是什么的时候,这个宏最有用处;

TIPS:

  • 如果一个函数保证会以某种方式改变其输出,不过这种改变方式是由运行测试时是星期几来决定的,这时最好的断言可能就是函数的输出不等于其输入。
  • assert_eq! 和 assert_ne! 宏在底层分别使用了 == 和 !=。
  • 当断言失败时,这些宏会使用调试格式打印出其参数,这意味着被比较的值必需实现了PartialEq 和 Debug trait
  • 对于自定义的结构体和枚举,需要实现PartialEq才能断言他们的值是否相等。
  • 需要实现Debug才能在断言失败时打印他们的值。
  • 由于这两个 trait 都是派生 trait,因此通常可以直接在结构体或枚举上添加#[derive(PartialEq, Debug)]标注。
  • 更多关于这些和其他派生 trait 的详细信息请参阅 这里 ;

1.5 使用 should_panic 检查 panic

  • 可以对测试程序添加#[should_panic]属性;
  • 即使满足条件也不会触发程序中的panic!宏;
fn add_two(a: i32) -> i32{

    if a < 0 {
        panic!("Par a must larger 0 but a is {}", a);
    }
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn it_adds() {
        let result = add_two(-5);
        assert_eq!(4, result);
    }
}
  • 当add_two传入的值小于0时会触发panic!宏;
  • 但是给it_adds额外添加一个#[should_panic]属性时依然会通过测试;

缺点

  • 添加should_panic属性之后的测试结果只是告诉我们代码产生了 panic;
  • should_panic在一些不是我们期望的原因而导致panic时也会通过 ;
  • 可以添加expected参数来关注我们所期待导致的panic;
fn add_two(a: i32) -> i32{

    if a < 0 {
        panic!("Par a must larger than 0 but a is {}", a);
    }else if a > 100{
        panic!("Par a must less than 100 but a is {}", a);
    }
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected = "Par a must larger than 0 but a is")]
    fn it_adds() {
        let result = add_two(101);
        assert_eq!(4, result);
    }
}
  • 为it_adds添加的[should_panic(expected = "Par a must larger than 0 but a is")]属性代表只有当panic!宏提示的字符串不包含Par a must larger than 0 but a is时会出错。
  • 传给add_two函数的值是101,因此走的是else if分支,panic!宏提示的错误是Par a must less than 100 but a is,这不包含expected中的子串,因此会报错。

在这里插入图片描述

二、将 Result<T, E> 用于测试

#[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>
  • 测试通过时返回 Ok(()),在测试失败时返回带有 String 的 Err;
  • 这样编写测试来返回 Result<T, E> 就可以在函数体中使用问号运算符,如此可以方便地编写测试,如果其中的任何操作返回 Err 值,则测试将失败;
  • 不能使用#[should_panic],要断言操作返回 Err 值,需要使用assert!(value.is_err())

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

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

相关文章

解决外网404:清除DNS缓存并配置host主机使用知名公共DNS服务

在 Windows 上清除/刷新 DNS 缓存 对于所有Windows版本&#xff0c;清除DNS缓存的过程都是相同的。你需要使用管理员权限打开命令提示符并运行ipconfig /flushdns。 浏览器清除DNS缓存 大多数现代的Web浏览器都有一个内置的DNS客户端&#xff0c;以防止每次访问该网站时…

vscode字符多行自动增长插件。

多行字符自动增长插件CharAutoIncre 当你使用shiftalt选中了多行,并输入了’1’,这时这几行都变成了’1’. 这时你可以选中&#xff08;shift左键&#xff09;为’1’的这几行, 接下来按下shiftaltq此时’1’变为了’12345’自增长的样式。 同时本插件支持字符’a-z,A-Z’。 目…

高考没考好焦虑怎么选计算机专业!一篇告诉你,推荐三个风口专业!想学计算机怎么选大学专业

高考成绩揭晓&#xff0c;几家欢喜几家愁。对于那些未能如愿考取理想分数的同学来说&#xff0c;未来似乎蒙上了一层阴影。尤其是在计算机专业如此热门的今天&#xff0c;低分考生是否还有机会在这个领域找到一席之地&#xff1f;本文将为你揭秘&#xff0c;即使高考成绩不理想…

Nature Microbiology丨VITA单细菌转录组测序技术助力深入解析奶牛瘤胃微生物组功能异质性

瘤胃微生物组一直以来都是研究相对不足但又极其复杂的微生物生态系统之一。瘤胃微生物能够有效降解植物纤维&#xff0c;将其转化为高质量的蛋白质产品&#xff0c;在这一过程中&#xff0c;由于微生物强烈的发酵&#xff0c;还会产生大量气体&#xff0c;其成分主要包括二氧化…

自动化产线设备联网,协同打造5G智慧工厂

1、需求背景 随着信息技术、物联网、人工智能等领域的飞速发展&#xff0c;智慧工厂成为制造业升级和转型的关键方向。在智慧工厂中&#xff0c;产线设备之间的实时通信和协同操作可以提高整个生产流程的自动化水平。 提升生产效率 通过稳定的网络连接&#xff0c;保证设备之…

编码在网络安全中的应用和原理

前言:现在的网站架构复杂&#xff0c;大多都有多个应用互相配合&#xff0c;不同应用之间往往需要数据交互&#xff0c;应用之间的编码不统一&#xff0c;编码自身的特性等都很有可能会被利用来绕过或配合一些策略&#xff0c;造成一些重大的漏洞。 什么是编码&#xff0c;为什…

软件功能测试和性能测试包括哪些测试内容?又有什么联系和区别?

软件功能测试和性能测试是保证软件质量和稳定性的重要手&#xff0c;无论是验证软件的功能正确性&#xff0c;还是评估软件在负载下的性能表现&#xff0c;这些测试都是必不可少的。 一、软件功能测试   软件功能测试是指对软件的各项功能进行验证和确认&#xff0c;确保软件…

51交通灯

一、基本原理 利用51单片机控制各个路口红绿灯及时间显示。 设计的重点&#xff1a; 1、各个路口红绿灯亮灭的规则&#xff0c;暂不考虑左转方向&#xff1b; 2、倒计时的实现&#xff0c;利用单片机的定时器进行计数得到秒信号&#xff1b; 3、时间显示&#xff1a;东西南…

【每日刷题】Day67

【每日刷题】Day67 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 23. 合并 K 个升序链表 - 力扣&#xff08;LeetCode&#xff09; 2. 1189. “气球” 的最大数量 - …

一篇搞定Spring,IOC容器,Bean管理,3.AOP底层原理和实现(收下吧,真的很详细)

1.Spring容器的概念 Spring是一个轻量级的框架&#xff0c;可以解决企业开发的复杂性&#xff0c;让开发效率提升&#xff0c;他核心的两个点是&#xff1a; 1.IOC IOC&#xff1a;在java中&#xff0c;我们程序员一般是去创建一个对象&#xff0c;那么有个问题就是耦合性太…

Apache DolphinScheduler查看版本信息

我找了半天&#xff0c;没有看到版本在哪里。然后我看配置&#xff0c;他要连接数据库&#xff0c;我去他存储数据库的表里面&#xff0c;看到了相关的版本信息。 cd /home/dolphinscheduler/dolphinscheduler/bin/env dolphinscheduler找到了里面的密码 版本是3.1.3

VMware挂载NAS存储异常处理

问题概述 由于非法关机或恢复&#xff0c;NFS存储可能会出现以下问题&#xff1a; 数据存储处于挂起状态或无法正常识别。虚拟机的配置文件或虚拟磁盘仍然注册在异常数据存储上。系统误认为有虚拟机在使用该数据存储。 问题对策 下面是详细的排查步骤和解决对策&#xff1a…

JS 实现Date日期格式的本地化

为了更好的更新多语言日期的显示&#xff0c;所以希望实现日期的本地化格式显示要求&#xff0c;常规的特殊字符型格式化无法满足显示要求&#xff0c;这里整理了几种我思考实现的本地化实现功能。 通过多方查找&#xff0c;总结了实现的思路主要有如下三个方向&#xff1a; 官…

ThinkPHP 的老漏洞仍然被攻击者钟情

研究人员发现安全领域出现了令人不安的趋势&#xff1a;攻击者不仅对新披露的漏洞十分感兴趣&#xff0c;对已知的漏洞也丝毫不放过&#xff0c;尽管有些漏洞已经存在了好些年头&#xff0c;攻击者仍然能够通过老漏洞成功完成攻击。 典型的例子就是 ThinkPHP 远程代码执行漏洞…

同三维T80004EHL-W-4K30 4K HDMI编码器,支持WEBRTC协议

输入&#xff1a;1路HDMI1路3.5音频&#xff0c;1路HDMI环出1路3.5音频解嵌输出 4K30超高清,支持U盘/移动硬盘/TF卡录制&#xff0c;支持WEBRTC协议&#xff0c;超低延时&#xff0c;支持3个点外网访问 1个主流1个副流输出&#xff0c;可定制选配POE供电模块&#xff0c;WEBR…

白酒:茅台镇白酒的文化内涵与传承意义

茅台镇白酒&#xff0c;作为中国的酒文化的代表之一&#xff0c;具有丰富的文化内涵和传承意义。而云仓酒庄豪迈白酒作为茅台镇的品牌之一&#xff0c;更是承载了深厚的文化底蕴和历史积淀。 首先&#xff0c;茅台镇白酒是中国的酒文化的重要组成部分。白酒在中国有着悠久的历史…

SRAM和DRAM

1.SRAM&#xff08;静态RAM&#xff09; 把存放一个二进制位的物理器件称为存储元&#xff0c;它是存储器最基本的构件。 地址码相同的多个存储元构成一个存储单元。 存储单元的集合构成存储体。 静态RAM的存储元是用双稳态触发器&#xff08;六晶体管MOS&#xff09;来记忆…

MyBatis查询两个字段,返回Map,一个字段作为key,一个字段作为value的实现

项目场景&#xff1a; 在使用MyBatis&#xff0c;我们经常会遇到这种情况&#xff1a;SELECT两个字段&#xff0c;需要返回一个Map&#xff0c;其中第一个字段作为key&#xff0c;第二个字段作为value。MyBatis的MapKey虽然很实用&#xff0c;但并不能解决这种场景。 问题描述 …

vue分类

先看效果 再看代码 <category-tab v-model"params.area" name"地区" :list"areaList" /><category-tab v-model"params.type" name"类型" :list"typeList" /><category-tab v-model"params.…

WPF/C#:如何将数据分组显示

WPF Samples中的示例 在WPF Samples中有一个关于Grouping的Demo。 该Demo结构如下&#xff1a; MainWindow.xaml如下&#xff1a; <Window x:Class"Grouping.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x&q…