这一章主要介绍 Rust 的测试。在 Rust 中,一个测试实际上就是一个函数,用于验证非测试代码的功能是否和预期一致。
测试函数体通常执行下面三个操作,也称
3a
操作:
- 准备数据或状态(arrange);
- 运行被测试的代码(act);
- 断言结果(assert)。
test
属性(attribute)进行标注
#[test]
,就将函数变成测试函数了cargo test
命令运行所有测试我们输入命令 cargo new test_demo --lib
创建一个项目,在 lib.rs
文件中看到这样的函数:
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}
这里 assert_eq!
是一个断言的宏,判断两个数是否相等。
然后运行 cargo test
,结果如下:
➜ ~/code/rust/test_demo git:(master) ✗ cargo test
Compiling test_demo v0.1.0 (/home/cherry/code/rust/test_demo)
Finished test [unoptimized + debuginfo] target(s) in 2.33s
Running unittests (target/debug/deps/test_demo-357c557c333f0e0d)
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests test_demo
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
我们单独编写一个带有 panic 的测试函数,测试结果如下:
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
#[test]
fn another() {
panic!("Test Failed!")
}
}
➜ ~/code/rust/test_demo git:(master) ✗ cargo test
Compiling test_demo v0.1.0 (/home/cherry/code/rust/test_demo)
Finished test [unoptimized + debuginfo] target(s) in 0.28s
Running unittests (target/debug/deps/test_demo-357c557c333f0e0d)
running 2 tests
test tests::another ... FAILED
test tests::it_works ... ok
failures:
---- tests::another stdout ----
thread 'tests::another' panicked at 'Test Failed!', src/lib.rs:11:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::another
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass '--lib'
我们用之前写的一个小例子,判断矩形 r1 是否能容纳矩形 r2(为了简单起见,只判断正着放,而不考虑斜着放进去)。
struct Rect {
x: u32,
y: u32,
}
impl Rect {
fn can_hold(&self, other: &Rect) -> bool {
let x = if other.x > other.y { other.x } else { other.y };
let y = if other.x < other.y { other.x } else { other.y };
self.x > x && self.y > y
}
}
测试函数:
#[test]
fn larger_can_hold_smaller() {
let r1 = Rect { x: 12, y: 8 };
let r2 = Rect { x: 5, y: 10 };
assert!(r1.can_hold(&r2));
}
显然返回是 true,测试通过。
==
和 !=
运算符assert!
宏,则只会告知测试结果而不会打印出两个参数的值我们再写一个简单的例子,将一个数加 2,判断两个值是否相等。
fn add_two(a: i32) -> i32 {
a + 2
}
#[test]
pub fn it_add_two() {
assert_eq!(4, add_two(2));
}
结果显然是正确的,若我们更改函数逻辑,把加 2 改成加 3,则运行测试结果为:
➜ ~/code/rust/test_demo git:(master) ✗ cargo test
Compiling test_demo v0.1.0 (/home/cherry/code/rust/test_demo)
Finished test [unoptimized + debuginfo] target(s) in 0.39s
Running unittests (target/debug/deps/test_demo-357c557c333f0e0d)
running 3 tests
test tests::it_add_two ... FAILED
test tests::it_works ... ok
test tests::larger_can_hold_smaller ... ok
failures:
---- tests::it_add_two stdout ----
thread 'tests::it_add_two' panicked at 'assertion failed: `(left == right)`
left: `4`,
right: `5`', src/lib.rs:37:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::it_add_two
test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass '--lib'
编译器将会自动给出两个参数的值(左值和右值),若将宏改成 assert_ne!
测试结果又将变成正确。
TODO