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

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() {
        // 这行应导致 panic
        Guess::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 运行所有测试

在项目根目录下执行:

bash 复制代码
cargo test

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

running 2 tests
test tests::it_works ... ok
test tests::it_fails ... FAILED

failures:
    tests::it_fails - panicked at 'Make this test fail'

6.2 只运行部分测试

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

bash 复制代码
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!

相关推荐
轩源源6 分钟前
数据结构——红黑树的实现
开发语言·数据结构·c++·算法·红黑树·单旋+变色·双旋+变色
Hello.Reader19 分钟前
Rust 命令行参数解析:以 minigrep 为例
开发语言·chrome·rust
Lanwarf-前端开发1 小时前
gis风场加载
开发语言·前端·javascript
自动驾驶小卡1 小时前
线性回归计算斜率原理及C++实现
c++·算法·回归·线性回归
程序员林北北1 小时前
【Golang学习之旅】gRPC 与 REST API 的对比及应用
java·开发语言·后端·学习·云原生·golang
钢铁男儿1 小时前
Python 字典(一个简单的字典)
开发语言·python
游王子1 小时前
Python Pandas(7):Pandas 数据清洗
开发语言·python·pandas
拉一次撑死狗1 小时前
LeetCode数学相关算法题(1)【C语言版】
c语言·算法·leetcode·职场和发展
DexterYttt2 小时前
P5788 【模板】单调栈
数据结构·c++·算法·蓝桥杯
0wioiw02 小时前
Python基础(SQLAlchemy)
java·开发语言·数据库