文章目录

Rust 测试:#[test] 属性与测试函数

发布于 2026-04-11 22:17:46 · 浏览 6 次 · 评论 0 条

Rust 测试:#[test] 属性与测试函数

Rust 语言内置了强大的测试功能,无需引入复杂的第三方库即可进行单元测试。掌握 #[test] 属性和相关的断言宏,是编写健壮 Rust 代码的基础。


1. 编写第一个测试函数

测试函数的本质是用于验证非测试代码是否按预期工作的函数。在 Rust 中,要让编译器识别一个函数为测试函数,必须对其添加 #[test] 属性。

创建一个新的二进制项目以进行练习:

cargo new adder

打开项目目录下的 src/main.rs 文件。在文件中,#[test] 属性通常位于测试函数的上方,紧接着是 fn 关键字定义的函数。测试函数内部通常使用断言宏来返回成功或失败。

输入以下代码到 src/main.rs 中:

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

在这段代码中,#[test] 标识了 it_works 是一个测试函数。assert_eq! 宏用于判断两个参数是否相等。


2. 运行测试

编写完测试代码后,需要通过 cargo test 命令来执行它们。该命令会编译项目并运行所有标记为 #[test] 的函数。

运行以下命令:

cargo test

终端会输出详细的测试信息。通常你会看到以下几部分内容:

  1. 编译信息:显示 Compiling adderFinished test
  2. **Running target/debug/deps/adder-...`:表示正在运行测试二进制文件。
  3. **Running unittests src/main.rs:表示正在运行src/main.rs` 中的单元测试。
  4. Test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out:这是测试摘要。如果代码正确,会显示 1 passed
  5. Doc-tests adder:这是文档测试的运行结果。

3. 使用断言宏验证结果

Rust 提供了多种断言宏来验证代码逻辑。最常用的是 assert!assert_eq!assert_ne!

3.1 assert!

assert! 宏接受一个布尔值参数。如果参数为 true,测试通过;如果为 false,测试会调用 panic! 导致失败。

编写一个检查逻辑的测试:

#[test]
fn test_boolean() {
    let is_rust_fun = true;
    assert!(is_rust_fun);
}

3.2 assert_eq!assert_ne!

这两个宏用于比较两个值是否相等或不相等。它们在失败时会自动打印出这两个值的实际内容,便于调试。

assert_eq! 判断相等,assert_ne! 判断不相等。

编写比较测试:

#[test]
fn test_addition() {
    let sum = 5 + 5;
    assert_eq!(sum, 10);
}

#[test]
fn test_not_equal() {
    let value = 100;
    assert_ne!(value, 99);
}

为了更清晰地对比这三个宏,请参考下表:

宏名 参数说明 用途 失败时的行为
assert! 一个布尔表达式 验证条件是否为真 调用 panic!
assert_eq! 两个表达式 验证两个值是否相等 调用 panic! 并打印两个值
assert_ne! 两个表达式 验证两个值是否不相等 调用 panic! 并打印两个值

4. 自定义失败信息

当断言失败时,Rust 允许添加自定义的错误信息,以便快速定位问题。这通过在断言宏参数的末尾添加格式化字符串来实现。

编写带有自定义信息的测试:

#[test]
fn test_with_message() {
    let name = "Ferris";
    assert!(
        name == "Rustacean",
        "名字应该是 'Rustacean' 但实际是 '{}'", name
    );
}

运行 cargo test,你会看到类似以下的错误输出:

thread 'test_with_message' panicked at '名字应该是 'Rustacean' 但实际是 'Ferris'', src/main.rs:4:5

5. 测试 Panic 处理

除了验证代码返回正确的结果,还需要验证代码在遇到错误时是否按预期崩溃(Panic)。这需要使用 #[should_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 }
    }
}

#[test]
#[should_panic]
fn test_greater_than_100() {
    Guess::new(200);
}

在这个例子中,Guess::new(200) 应该会导致程序崩溃。因为添加了 #[should_panic] 属性,只要该函数触发了 Panic,测试就算通过。

5.1 精确匹配 Panic 信息

为了确保 Panic 是由特定的原因引起的,可以在 #[should_panic] 属性中添加 expected 参数。

修改上述测试代码,使其检查特定的错误信息:

#[test]
#[should_panic(expected = "Guess value must be between 1 and 100")]
fn test_greater_than_100_precise() {
    Guess::new(200);
}

只有当 Panic 信息包含 "Guess value must be between 1 and 100" 这个子串时,测试才会通过。


6. 运行部分测试

随着项目变大,测试数量会随之增加,每次运行所有测试会浪费时间。cargo test 允许通过参数过滤需要运行的测试。

6.1 运行单个测试

运行指定名称的测试,只需将测试名称作为参数传递给 cargo test

cargo test it_works

这将只运行名为 it_works 的测试函数。

6.2 运行部分匹配的测试

如果传递的名称匹配了多个测试,所有匹配的测试都会运行。

假设你有以下测试函数:

  • test_addition
  • test_subtraction

运行以下命令:

cargo test test_add

这将同时运行 test_addition,因为它的名称包含了 test_add


7. 忽略某些测试

有时某些测试非常耗时(例如涉及数据库操作或复杂计算),你可能希望在大多数开发过程中跳过它们。这可以使用 #[ignore] 属性实现。

添加 #[ignore] 属性到耗时测试的上方:

#[test]
#[ignore]
fn expensive_test() {
    // 耗时很长的代码
}

运行 cargo test,你会发现 expensive_test 被标记为 ignored,默认不会执行。

如果确实需要运行被忽略的测试,可以使用 --ignored 参数:

cargo test -- --ignored

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文