Rust 测试指南:从入门到进阶

devtools/2025/2/13 3:33:34/

1. 测试基础:#[test] 属性

Rust 测试的基本单位是函数。只要在一个函数前面标注 #[test] 属性,那么在运行 cargo test 时,Rust 会自动识别并执行它。例如,新建一个库工程 addercargo new adder --lib,在 src/lib.rs 中可能会有如下的默认测试模板:

rust">#[cfg(test)]
mod tests {use super::*;#[test]fn it_works() {let result = add(2, 2);assert_eq!(result, 4);}
}pub fn add(a: i32, b: i32) -> i32 {a + b
}
  • #[cfg(test)]:告诉编译器该模块仅在测试编译时包含。
  • #[test]:标注该函数是一个测试函数,测试运行器会自动收集、执行它们。
  • assert_eq!:断言左右两边相等,若不相等则测试失败。

运行 cargo test,会看到测试通过或失败的详细信息。由此可见,Rust 测试非常直观:只需写好函数体并添加 #[test] 标记即可。

2. 断言宏:assert!assert_eq!assert_ne!

2.1 assert!

assert! 宏主要用于断言一个布尔表达式为 true,如果表达式结果为 false,则触发 panic!,导致测试失败。例如:

rust">#[test]
fn test_assert() {let condition = (2 + 2) == 4;assert!(condition, "2 + 2 应该等于 4");
}

如果 conditionfalseassert! 会打印指定的出错信息。

2.2 assert_eq!assert_ne!

这两个宏分别用于断言两者相等或不相等。当断言失败时,它们会打印出左值与右值,方便我们快速定位问题:

rust">#[test]
fn test_assert_eq_ne() {let x = 5;let y = 3 + 2;assert_eq!(x, y, "x 与 y 应该相等");assert_ne!(x, 10, "x 不应该为 10");
}

与直接使用 assert!(x == y) 相比,assert_eq! 在失败时能更详细地输出左右值的具体内容。

3. 自定义失败信息

当断言失败时,希望打印更具可读性或指导性的错误信息时,可以在宏的后面添加自定义文字或使用格式化字符串:

rust">#[test]
fn test_custom_message() {let greeting = "Hello, Rust!";assert!(greeting.contains("Rust"),"Greeting 中不包含预期关键字,实际输出: `{}`",greeting);
}

当测试失败时,你将看到含有 greeting 变量具体值的提示信息,能更快定位问题。

4. 检查代码是否会触发 panic!#[should_panic]

有时我们需要测试错误场景,例如希望函数在遇到非法输入时能自动 panic!。可通过给测试函数添加 #[should_panic] 属性来验证:

rust">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 test_guess_out_of_range() {// 这行应导致 panicGuess::new(200);}
}

此时,如果 Guess::new(200) 不触发 panic!,测试将报告失败。
另外,还可以使用 #[should_panic(expected = "substring")] 来匹配特定的错误信息,从而确保 panic! 的原因与我们预期一致。

5. 使用 Result<T, E> 编写测试

除了直接使用断言触发 panic!,我们也可以让测试函数返回 Result<(), String>。当返回 Err(...) 时,测试视为失败;返回 Ok(()) 时,测试视为成功。这对于需要在测试中使用 ? 运算符(即在测试内做错误传播)会很有帮助:

rust">#[test]
fn it_works_with_result() -> Result<(), String> {let result = add(2, 2);if result == 4 {Ok(())} else {Err(format!("期待 4,实际得到: {}", result))}
}
  • 注意:如果使用 Result<T, E>,则不能再搭配 #[should_panic]
  • 当需要断言某个操作返回 Err 时,最直接的做法是 assert!(value.is_err()),而不是给返回结果加 ?

6. 运行与管理测试

6.1 运行所有测试

在项目根目录下执行:

cargo test

Rust 会构建测试二进制并执行所有标有 #[test] 的函数,输出通过与失败的测试结果统计。结果示例:

running 2 tests
test tests::it_works ... ok
test tests::it_fails ... FAILEDfailures:tests::it_fails - panicked at 'Make this test fail'

6.2 只运行部分测试

如果想只运行指定名称匹配的测试,可以在 cargo test 后加上测试名称关键字,支持模糊匹配:

cargo test it_works

只会运行包含 it_works 子串的测试函数。

6.3 忽略测试

有些测试耗时较长或依赖外部环境,临时不想执行时,可以使用 #[ignore] 标记。这样默认不会运行这些测试,除非指定 cargo test -- --ignored 或者 cargo test -- --include-ignored

rust">#[test]
#[ignore]
fn expensive_test() {// 测试内容
}

7. 总结

Rust 的测试系统内置于语言与工具链之中,让开发者可以在编写、维护与运行测试时拥有非常高的效率与安全感。本篇文章主要分享了以下几点:

  1. 测试函数的基本形式:使用 #[test] 标注,并通过 cargo test 执行测试。
  2. 常用断言宏
    • assert! 适合普通布尔表达式;
    • assert_eq!assert_ne! 在断言相等/不相等时带来更多调试信息。
  3. 自定义失败信息:通过字符串拼接或格式化输出,提升调试效率。
  4. 检查 panic! 场景:使用 #[should_panic],并可利用 expected 参数匹配特定信息。
  5. 使用 Result<T, E>:让测试函数返回 Result,可以灵活地利用 ? 运算符处理错误。
  6. 测试管理:灵活过滤测试、忽略测试、性能基准测试(需夜ly Rust),都能帮助你更好地组织与维护测试集。

有了这些基础,你就可以非常方便地在 Rust 项目中为关键功能编写自动化测试,并在项目规模不断扩大时维持高质量与高可维护性。希望这篇文章能让你更好地理解并掌握 Rust 测试体系,编写出更加可靠的代码!

祝你写测试愉快,玩转 Rust!


http://www.ppmy.cn/devtools/158364.html

相关文章

使用 Python-pptx 库提取 PPTX 文件中的结构与文字

是的&#xff0c;使用 python-pptx 库是提取 PPTX 文件中结构和文字的理想选择&#xff0c;原因如下&#xff1a; 专门处理 PPTX 格式 python-pptx 是一个专门为处理 PPTX 文件&#xff08;.pptx 格式&#xff09;而设计的 Python 库。 它可以读取和操作 PPTX 文件的内部结构…

idea插件开发,如何获取idea设置的系统语言

手打不易&#xff0c;如果转摘&#xff0c;请注明出处&#xff01; 注明原文&#xff1a;https://zhangxiaofan.blog.csdn.net/article/details/145578160 版本要求 大于 2024.3 错误用法 网上有的说使用&#xff1a;UIUtil com.intellij.util.ui.UIUtil 代码示例&#xf…

c/c++蓝桥杯经典编程题100道(17)二叉树遍历

二叉树遍历 ->返回c/c蓝桥杯经典编程题100道-目录 目录 二叉树遍历 一、题型解释 二、例题问题描述 三、C语言实现 解法1&#xff1a;递归前序遍历&#xff08;难度★&#xff09; 解法2&#xff1a;迭代中序遍历&#xff08;难度★★&#xff09; 解法3&#xff1a…

Arrays工具类详解

目录 1. Arrays.toString() 方法 2. Arrays.deepToString() 方法 3. Arrays.equals(int[ ] arr1, int[ ] arr2) 方法 4. Arrays.equals(Object[] arr1, Object[] arr2) 方法 5. Arrays.deepEquals(Object[] arr1, Object[] arr2) 方法 6. Arrays.sort(int[] arr) 方法 7…

深度解析:使用ChromeDriver和webdriver_manager实现无头浏览器爬虫

问题背景 在现代网络爬虫的实践中&#xff0c;动态网页的内容加载和复杂的反爬虫机制使得数据采集变得愈发困难。传统的静态网页爬取方法已无法满足需求&#xff0c;尤其是在需要模拟用户行为、处理JavaScript渲染的场景下。为此&#xff0c;采用无头浏览器&#xff08;Headle…

【c++】字符串 string 以及与右值结合

【c】字符串 string 以及与右值结合 std::string 类介绍 std::string 是 C 标准库中的一个用于处理字符串的类&#xff0c;定义在头文件 <string> 中。std::string 封装了一个可变长度的字符数组&#xff0c;可以动态地存储和操作文本字符串。 在使用char类型指针指向…

2025影视泛目录站群程序设计_源码二次开发新版本无缓存刷新不变实现原理

1. 引言 本设站群程序计书旨在详细阐述苹果CMS泛目录的创新设计与实现&#xff0c;介绍无缓存刷新技术、数据统一化、局部URL控制及性能优化等核心功能&#xff0c;以提升网站访问速度和用户体验。 2. 技术概述 2.1 无缓存刷新技术 功能特点&#xff1a; 内容不变性&#x…

halcon三维点云数据处理(十四)在3D场景中去除背景的三种方式

目录 一、基于二值化去除背景二、基于参考平面去除背景三、基于参考场景去除背景四、总结 一、基于二值化去除背景 在2D空间中&#xff1a;reduce_object_model_3d_by_view (Regions, ObjectModel3DScene, [], [], ObjectModel3DReduced) 首先二值化得到region&#xff0c;然后…