喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
11.10.1. 什么是集成测试
在Rust里,集成测试完全位于被测试库的外部。集成测试调用库的方式和其他代码一样,这也意味着集成测试只能调用对外公开的API。
集成测试的目的是验证被测试库的多个部分能否正确地一起工作,这一点有别于单元测试,单元测试比较小也比较专注,每次只对一个模块进行隔离的测试,还可以测试私有的(private)接口。
有时候能够独立运行的一些单元代码在合在一起运用时也会发生问题,集成测试正是为了今早发现和解决这种问题存在的。所以说,集成测试的覆盖率很重要。
11.10.2. tests目录
创建集成测试需要先创建tests目录。
这个目录是与src
目录并列,cargo
会自动在这个目录下寻找集成测试文件。在这个目录下可以创建任意多的集成测试文件,cargo
会在编译时把每个测试文件都处理为一个单独的包,也就是一个单独的crate
。
下面来演示一下创建集成测试文件:
1. 创建tests目录
在与src
同级的目录下创建名为tests
的文件夹:
2. 创建测试文件
在tests
目录下创建.rs
的测试文件,给它取个名字,这里我起的是integration_test.rs
:
3. 把测试代码移到测试文件里
以上一篇文章的代码为例(lib.rs
):
rust">pub fn add_two(a: usize) -> usize {internal_adder(a, 2)
}fn internal_adder(left: usize, right: usize) -> usize {left + right
}#[cfg(test)]
mod tests {use super::*;#[test]fn internal() {let result = internal_adder(2, 2);assert_eq!(result, 4);}
}
由于每一个集成测试文件都是一个单独的crate,所以这个文件(integration_test.rs
)想要测试lib.rs
这个crate的内容就得先导入作用域。
在这个例子中,由于我给这个项目命名为RustStudy,所以这个package(包)的名字就是RustStudy,如果你不清楚可以到你的cargo.toml
里看name
这个参数。在这个例子中,导入作用域写:use RustStudy;
即可,如果你想指定到具体的函数也行。
导入完后直接写测试函数就可以,不需要写#[cfg(test)]
,因为tests
目录下的代码只会在运行cargo test
的时候被执行,只需要给测试函数标注#[test]
即可。
整体代码如下(integration_test.rs
):
rust">use RustStudy;#[test]
fn it_adds_two() {let result = RustStudy::add_two(2);assert_eq!(result, 4);
}
输出:
$ cargo testCompiling adder v0.1.0 (file:///projects/adder)Finished `test` profile [unoptimized + debuginfo] target(s) in 1.31sRunning unittests src/lib.rs (target/debug/deps/adder-1082c4b063a8fbe6)running 1 test
test tests::internal ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning tests/integration_test.rs (target/debug/deps/integration_test-1082c4b063a8fbe6)running 1 test
test it_adds_two ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sDoc-tests adderrunning 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
可以看到,这个输出显示运行了两个测试,这是因为一个是来自lib.rs
的测试(单元测试),一个是来自integration_test.rs
的测试(集成测试)。
11.10.3. 运行指定的集成测试
运行一个特定的集成函数,可以使用cargo test 指定的函数名
;运行某个测试文件内的所有测试函数,可以使用cargo test --test 文件名
。
看个例子:
现在tests
下有两个文件,如果我只希望运行integration_test.rs
里的测试函数,那么就输入指令:
cargo test --test integration_test
11.10.4. 集成测试中的子模块
由于tests
目录下的每个文件被编译成单独的crate,所以这些文件互不共享行为(与src
目录下的文件规则不同)。
那如果我想将测试函数中重复出现的逻辑提取到一个helper函数中,避免代码重复,该怎么写呢?
举个例子,我在tests
目录下写了common.rs
这个文件用于存储helper函数:
试试执行测试:
$ cargo testCompiling adder v0.1.0 (file:///projects/adder)Finished `test` profile [unoptimized + debuginfo] target(s) in 0.89sRunning unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)running 1 test
test tests::internal ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning tests/common.rs (target/debug/deps/common-92948b65e88960b4)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning tests/integration_test.rs (target/debug/deps/integration_test-92948b65e88960b4)running 1 test
test it_adds_two ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sDoc-tests adderrunning 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
可以看到,在测试结果中出现了对common.rs
的测试。但因为common.rs
是用来存储helper函数的,所以它本身不需要也没必要被测试,这种写法是错误的。
正确的做法是在tests
目录下创建一个common
目录,在里面创建mod.rs
,把helper函数都放到这里面来,把原来的common.rs
删掉即可:
这实际上是另外一种可以被Rust理解的命名规范,Rust不会把common
模块视作为集成测试文件,而在测试输出中也不会出现common
了,因为tests
下的子目录不会被视为单独的crate进行编译。
如果要在集成测试文件中使用这里面的内容,只需要在文件开头写mod 文件夹名;
即可,在这个例子中就是mod common;
。使用时写common::你想要的函数
,在这个例子中就是common::setup()
11.10.5. 针对binary crate的集成测试
如果项目是二进制包(binary crate),也就是只含有src/main.rs
没有src/lib.rs
,就不可以在tests
目录下创建集成测试,即使有,也无法把main.rs
的函数导入作用域。因为只有library crate(也就是有lib.rs
)才能暴露函数给其它crate用。
binary crate意味着独立运行。因此,Rust的binary项目通常会把这些逻辑都放在lib.rs
里,而在main.rs
里只有简单的调用。这样做项目就会被视为library crate,就可以使用集成测试来检查代码。