【Rust自学】11.10. 集成测试

【Rust自学】11.10. 集成测试 11.10 集成测试11.10.1. 什么是集成测试在Rust里集成测试完全位于被测试库的外部。集成测试调用库的方式和其他代码一样这也意味着它们只能调用公共API。集成测试的目的是验证库的多个部分能否正确地一起工作。这一点有别于单元测试单元测试比较小也比较专注。单元测试单独测试一个模块还可以测试私有接口。有时单独运行没问题的代码合在一起使用时仍可能出问题。集成测试正是为了尽早发现并解决这类问题而存在的。因此集成测试的覆盖率很重要。11.10.2.tests目录要创建集成测试首先创建tests目录。这个目录与src并列cargo会自动在那里寻找集成测试文件。你可以在这个目录下创建任意数量的集成测试文件。编译时cargo会把每个测试文件当作一个单独的包也就是一个单独的crate。下面演示如何创建集成测试文件1. 创建tests目录在src旁边创建一个名为tests的文件夹2. 创建测试文件在tests下创建一个.rs测试文件并给它取名。这里我用的是integration_test.rs3. 把测试代码移到测试文件里以 11.9. 单元测试 的代码为例lib.rspub 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如果想测试那个crate就必须先把lib.rs的内容导入作用域。在这个例子中由于我把项目命名为RustStudy所以包名也是RustStudy。如果不清楚可以到Cargo.toml里看name字段。在这个例子中可以写use RustStudy;来导入如果想导入某个具体函数也可以。导入之后可以直接写测试函数。不需要写#[cfg(test)]因为tests目录下的代码只有在执行cargo test时才会运行。只需要给测试函数标注#[test]即可。完整代码如下integration_test.rsuse RustStudy; #[test] fn it_adds_two() { let result RustStudy::add_two(2); assert_eq!(result, 4); }输出$ cargo test Compiling RustStudy v0.1.0 (file:///projects/RustStudy) Finished test profile [unoptimized debuginfo] target(s) in 0.15s Running unittests src/lib.rs (target/debug/deps/RustStudy-48a2c23cb22e1ddc) running 1 test test tests::internal ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/integration_test.rs (target/debug/deps/integration_test-e60608d740742c0c) running 1 test test it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests RustStudy running 0 tests test 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 test_name。要运行某个测试文件中的所有测试函数使用cargo test --test file_name。看个例子现在tests下有两个文件。如果我只想运行integration_test.rs里的测试函数可以运行cargo test --test integration_test11.10.4. 集成测试中的子模块因为tests下的每个文件都被编译成单独的crate所以这些文件彼此不共享行为这与src下的文件不同。那么如果想把测试函数中重复的逻辑提取到一个helper函数里以避免重复该怎么写呢例如我在tests下创建了common.rs来存放helper函数试着运行测试$ cargo test Compiling RustStudy v0.1.0 (file:///projects/RustStudy) Finished test profile [unoptimized debuginfo] target(s) in 0.13s Running unittests src/lib.rs (target/debug/deps/RustStudy-48a2c23cb22e1ddc) running 1 test test tests::internal ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/common.rs (target/debug/deps/common-5306c3915df25199) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/integration_test.rs (target/debug/deps/integration_test-e60608d740742c0c) running 1 test test it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests RustStudy running 0 tests test 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 folder_name;。在这个例子中就是mod common;。使用时写common::your_function。在这个例子中就是common::setup()。11.10.5. 针对二进制crate的集成测试如果项目是二进制crate也就是只有src/main.rs而没有src/lib.rs就不能在tests下创建集成测试即使创建了也无法把main.rs里的函数导入作用域。因为只有库crate也就是有lib.rs的才能把函数暴露给其他crate使用。二进制crate意味着独立运行。因此Rust的二进制项目通常会把这些逻辑放在lib.rs里而在main.rs里只保留简单调用。这样项目就会被视为库crate就可以用集成测试来检查代码。