【Rust自学】11.4. 用should_panic检查恐慌
📅 2026/7/23 18:56:14
👁️ 阅读次数
📝 编程学习
11.4 用should_panic检查恐慌
11.4.1. 验证错误处理的情况
除了验证代码是否返回正确的值,测试还需要验证代码是否如预期处理了错误情况。例如,可以编写一个测试来验证代码是否在特定条件下发生恐慌。
这类测试需要额外的should_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 } } } #[cfg(test)] mod tests { use super::*; #[test] #[should_panic] fn greater_than_100() { Guess::new(200); } }- 结构体
Guess有一个类型为i32的字段value。它提供关联函数new用于创建Guess实例,但前提是传给new的参数在1到100之间;否则就会恐慌。 greater_than_100测试函数给new传入大于100的值。这时应该发生恐慌,所以给这个测试函数加了should_panic属性,也就是写#[should_panic]。
测试结果:
$ cargo test Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished `test` profile [unoptimized + debuginfo] target(s) in 0.12s Running unittests src/lib.rs (target/debug/deps/guessing_game-bdc6b9a45c563cc0) running 1 test test tests::greater_than_100 - should panic ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests guessing_game running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s现在故意引入一个bug,把new里的value > 100检查去掉:
pub struct Guess { value: i32, } impl Guess { pub fn new(value: i32) -> Guess { if value < 1 { panic!("Guess value must be between 1 and 100, got {value}."); } Guess { value } } } #[cfg(test)] mod tests { use super::*; #[test] #[should_panic] fn greater_than_100() { Guess::new(200); } }这时测试函数中的Guess::new(200);不会恐慌。但因为函数加了should_panic标记,本应恐慌却没有恐慌的测试就会失败:
$ cargo test Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished `test` profile [unoptimized + debuginfo] target(s) in 0.09s Running unittests src/lib.rs (target/debug/deps/guessing_game-bdc6b9a45c563cc0) running 1 test test tests::greater_than_100 - should panic ... FAILED failures: ---- tests::greater_than_100 stdout ---- note: test did not panic as expected at src/lib.rs:21:8 failures: tests::greater_than_100 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--lib`11.4.2. 让should_panic更精确
有时使用should_panic的测试会有点含糊,因为它们只能说明代码是否发生了恐慌,即使这个恐慌并不是程序员预期的那个。
为了让测试更精确,可以为should_panic添加一个可选的expected参数。这样程序就会检查失败消息中是否包含指定的文字。
看个例子:
pub struct Guess { value: i32, } impl Guess { pub fn new(value: i32) -> Guess { if value < 1 { panic!( "Guess value must be greater than or equal to 1, got {value}." ); } else if value > 100 { panic!( "Guess value must be less than or equal to 100, got {value}." ); } Guess { value } } } #[cfg(test)] mod tests { use super::*; #[test] #[should_panic(expected = "less than or equal to 100")] fn greater_than_100() { Guess::new(200); } }- 上面的结构体稍作了修改:
new里value < 1和value > 100的情况现在使用两条不同的恐慌信息。 - 给
should_panic添加了expected参数,=后面的文字就是期待的错误信息。只有函数发生了恐慌,并且恐慌信息包含期待的文字,测试才通过;否则就失败。
这个程序肯定能通过。
用同样的套路手动引入错误。例如,把new里小于1和大于100时的恐慌信息交换一下:
pub struct Guess { value: i32, } impl Guess { pub fn new(value: i32) -> Guess { if value < 1 { panic!( "Guess value must be less than or equal to 100, got {value}." ); } else if value > 100 { panic!( "Guess value must be greater than or equal to 1, got {value}." ); } Guess { value } } } #[cfg(test)] mod tests { use super::*; #[test] #[should_panic(expected = "less than or equal to 100")] fn greater_than_100() { Guess::new(200); } }测试结果:
$ cargo test Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished `test` profile [unoptimized + debuginfo] target(s) in 0.08s Running unittests src/lib.rs (target/debug/deps/guessing_game-bdc6b9a45c563cc0) running 1 test test tests::greater_than_100 - should panic ... FAILED failures: ---- tests::greater_than_100 stdout ---- thread 'tests::greater_than_100' (457156) panicked at src/lib.rs:12:13: Guess value must be greater than or equal to 1, got 200. note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: panic did not contain expected string panic message: "Guess value must be greater than or equal to 1, got 200." expected substring: "less than or equal to 100" failures: tests::greater_than_100 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--lib`失败信息表明测试确实发生了恐慌,但恐慌消息没有包含期待的字符串less than or equal to 100。在这种情况下,我们实际收到的恐慌信息是Guess value must be greater than or equal to 1, got 200.。根据这些信息就足以修复这个bug。
编程学习
技术分享
实战经验