3 个稳定版本
1.2.0 | 2023年7月20日 |
---|---|
1.1.0 | 2022年2月1日 |
1.0.0 | 2022年1月29日 |
#618 在 测试
432 次每月下载
用于 2 crates
25KB
265 行
测试辅助函数应该是简单的。
你不希望担心测试套件中的错误或意外行为导致测试无声地通过。因此,simple_test_case
的目标是尽量减少编写参数化测试的模板代码,而不做更多。
test_case
属性宏会为你生成多个测试函数,这些函数通过提供的输入进行参数化。你仍然需要提供 #[test]
属性(或类似 #[tokio::test]
的替代品)和所有测试用例必须在你希望应用的任何其他属性宏之前提供。
就是这样。
没有对自定义断言、固定值等提供额外的支持。但是,如果你想或需要更复杂的测试设置,只要遵循以下建议,额外的属性宏应该可以很好地与 simple_test_case
一起使用。
使用方法
有效
这里,#[test]
属性在所有 test_case
实例之后提供。这将有效。
use simple_test_case::test_case;
fn double(n: usize) -> usize {
n * 2
}
#[test_case(1, 2; "case 1")]
#[test_case(3, 6; "case 2")]
#[test]
fn double_test(n: usize, double: usize) {
assert_eq!(double(n), double)
}
无效
这里,#[test]
属性在所有 test_case
实例之前提供。这将导致编译器抱怨用作测试的函数不允许有任何参数。
use simple_test_case::test_case;
fn double(n: usize) -> usize {
n * 2
}
#[test]
#[test_case(1, 2; "case 1")]
#[test_case(3, 6; "case 2")]
fn double_test(n: usize, double: usize) {
assert_eq!(double(n), double)
}
其他属性
test_case
保留其下所有属性,并将它们转发给生成的单个测试函数。以下是一个示例,标准库中的 should_panic
属性正如所示一样工作得很好(请确保首先按上述描述提供您的测试用例)
use simple_test_case::test_case;
#[test_case(1, 2; "case 1")]
#[test_case(3, 6; "case 2")]
#[test]
#[should_panic(expected = "this works")]
fn panic_test(n: usize, double: usize) {
assert_eq!(double(a), b);
panic!("this works")
}
异步测试
异步测试与其他属性支持的方式相同:首先添加您的测试用例,然后在下面应用您选择的异步测试宏。
use simple_test_case::test_case;
async fn async_double(n: usize) -> usize {
n * 2
}
#[test_case(1, 2; "case 1")]
#[test_case(3, 6; "case 2")]
#[tokio::test]
async fn double_test(n: usize, double: usize) {
assert_eq!(double(n).await, double)
}
它是如何工作的?
建议您阅读宏本身的源代码(宏及其相关辅助函数的代码少于150行),但基本思路如下
- 收集所有
test_case
(或simple_test_case::test_case
)属性,每个属性将一组函数参数映射到测试用例名称。 - 对于每个测试用例,创建原始测试函数的副本,并将函数参数替换为函数体顶部的显式变量绑定。
- 将每个用例写入一个新模块中的单独测试,该模块的名称使用原始测试函数名称。
您可以使用 cargo expand 来查看生成的测试示例,如下所示(使用 examples
目录中提供的示例)
$ cargo expand --example=expand_me --tests
Compiling simple_test_case v0.1.0 (/home/innes/repos/personal/simple_test_case)
Finished test [unoptimized + debuginfo] target(s) in 0.12s
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use simple_test_case::test_case;
mod example {
#[allow(unused_imports)]
use super::*;
extern crate test;
#[cfg(test)]
#[rustc_test_marker]
pub const small_example: test::TestDescAndFn = test::TestDescAndFn {
desc: test::TestDesc {
name: test::StaticTestName("example::small_example"),
ignore: false,
allow_fail: false,
compile_fail: false,
no_run: false,
should_panic: test::ShouldPanic::No,
test_type: test::TestType::Unknown,
},
testfn: test::StaticTestFn(|| test::assert_test_result(small_example())),
};
fn small_example() {
let a: usize = 1;
let b: usize = 2;
if !(a < b) {
::core::panicking::panic("assertion failed: a < b")
}
}
extern crate test;
#[cfg(test)]
#[rustc_test_marker]
pub const large_example: test::TestDescAndFn = test::TestDescAndFn {
desc: test::TestDesc {
name: test::StaticTestName("example::large_example"),
ignore: false,
allow_fail: false,
compile_fail: false,
no_run: false,
should_panic: test::ShouldPanic::No,
test_type: test::TestType::Unknown,
},
testfn: test::StaticTestFn(|| test::assert_test_result(large_example())),
};
fn large_example() {
let a: usize = 100;
let b: usize = 200;
if !(a < b) {
::core::panicking::panic("assertion failed: a < b")
}
}
}
#[allow(dead_code)]
fn main() {}
#[rustc_main]
pub fn main() -> () {
extern crate test;
test::test_main_static(&[&small_example, &large_example])
}
依赖项
~280–730KB
~17K SLoC