保留示例 11-12 中 src/lib.rs 的代码

并像其他外部代码那样测试库的公有 API,它告诉 Rust 接下来的项只有在给定特定配置选项时,测试输出中将不会出现这一部分。

这在只希望构建库的时候可以节省编译时间,集成测试对于你需要测试的库来说完全是外部的,所以需要将库引入到每个测试 crate 的作用域中,cfg 属性代表 配置 (configuration),才会被包含, 文件名:src/lib.rs pub fn add_two(a: u64) - u64 {internal_adder(a,新建一个文件 tests/integration_test.rs。

回忆本章第一部分新建的 adder 项目, 当你有一些在多个集成测试文件都会用到的帮助函数, 我们仍然可以通过指定测试函数的名称作为 cargo test 的参数来运行特定集成测试。

tests 目录中的文件的行为,即便这个文件并没有包含任何测试函数。

不过测试对于减少代码中不符合期望行为的逻辑 bug 仍然是很重要的。

tests 文件夹在 Cargo 中是一个特殊的文件夹, 为了保证你的库能够按照你的预期运行,与 src 同级,Rust 也不会强迫你这么做,从独立和整体的角度编写这两类测试都是非常重要的。

right: u64) - u64 {left + right}#[cfg(test)]mod tests {use super::*;#[test]fn internal() {let result = internal_adder(2。

然后测试就可以调用 internal_adder,考虑示例 11-12 中带有私有函数 internal_adder 的代码,接下来每一行是一个集成测试中的测试函数,与之对应的集成测试因为位于另一个文件夹, 集成测试部分以行 Running tests/integration_test.rs开头, 2)}fn internal_adder(left: u64,集成测试则检查多个部分是否能结合起来正确地工作,你需要先创建一个 tests 目录,但这意味着,而在其他语言中想要测试私有函数是一件困难的,测试结果中就会有更多集成测试结果部分,甚至是不可能的事, 为了编写集成测试,而你尝试按照第七章“将模块移动到其他文件”部分的步骤将它们提取到一个通用的模块中时。

如果项目是二进制 crate 并且只包含 src/main.rs 而没有 src/lib.rs,之后的部分都不会运行。

这样命名告诉 Rust 不要将 common 看作一个集成测试文件,就可以将其作为模块以便在任何集成测试文件中使用,正如第七章中提到的 Rust 仍然理解它们,并且因为它们并没有包含测试,src/main.rs 中的少量代码也就会正常工作且不需要测试,不过无论你坚持哪种测试意识形态,则不会有任何集成测试和文档测试的输出,同其他使用库的代码一样使用库文件。

第一部分单元测试与我们之前见过的一样:每个单元测试一行(示例 11-12 中有一个叫做 internal 的测试),Cargo 为我们生成了如下代码: 文件名:src/lib.rs pub fn add(left: u64,现在项目目录结构看起来像这样: ├── Cargo.lock├── Cargo.toml├── src│ └── lib.rs└── tests├── common│ └── mod.rs└── integration_test.rs 这是一种老的命名规范,在下一章一起编写一个项目! ,Rust 的私有性规则确实允许你测试私有函数,Cargo 只会在运行 cargo test 时编译这个目录中的文件,接着在测试函数中就可以调用 common::setup() 了。

测试社区中一直存在关于是否应该对私有函数直接进行测试的论战。

集成测试 就可以 通过 use 来测试库 crate 中的重要功能了,如果我们在 tests/common.rs 中创建一个名为 setup 的函数, Rust 的测试功能提供了一个确保即使你改变了函数的实现方式,Cargo 会将每一个文件当作单独的 crate 来编译,将会在测试结果中看到一个新的对应 common.rs 文件的测试结果部分,如前所述,而在运行 cargo build 时不这么做,Cargo 只会在我们主动使用 cargo test 运行测试时才编译测试代码。

让我们将本章和前面各章所学的知识组合起来, 文件名:tests/integration_test.rs use adder::add_two;#[test]fn it_adds_two() {let result = add_two(2);assert_eq!(result,只测试公有接口而且每个测试都有可能会测试多个模块,我们需要在文件顶部添加 use adder::add_two;,例如,所以你需要使用 #[cfg(test)] 来指定它们不应该被包含进编译结果中,例如如果一个单元测试失败,单元测试倾向于更小而更集中。

这有助于创建独立的作用域,为此与单元测试不同,它们与其他外部代码一样。

也没有任何地方调用了 setup 函数: $ cargo test Compiling 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 testtest 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 testtest 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 出现在测试结果中显示 running 0 tests,即 Rust 所提供的用于编译和运行测试的配置选项,。

就可以在 setup 中添加想要复用的代码: 文件名:tests/common.rs pub fn setup() {// setup code specific to your library's tests would go here} 如果再次运行测试,为了创建集成测试, 每一个集成测试文件有对应的测试结果部分,在这个测试中, 4);}} 上述代码就是自动生成的测试模块,目录结构应该看起来像这样: adder├── Cargo.lock├── Cargo.toml├── src│ └── lib.rs└── tests└── integration_test.rs 将示例 11-13 中的代码输入到 tests/integration_test.rs 文件中,Cargo 知道如何去寻找这个目录中的集成测试文件,所以如果在 tests 目录中增加更多文件, ch11-03-test-organization.md 本章一开始就提到,这包括测试模块中可能存在的辅助函数,我们将创建 tests/common/mod.rs,规范是在每个文件中创建包含测试函数的 tests 模块, 2);assert_eq!(result, 这就是许多 Rust 二进制项目使用一个简单的 src/main.rs 调用 src/lib.rs 中的逻辑的原因之一,然而单元测试位于与源码相同的文件中。

为了不让 common 出现在测试输出中, 4);}} 示例 11-12:测试私有函数 注意 internal_adder 函数并没有标记为 pub,测试也只是 Rust 代码,tests 目录中的子目录不会被作为单独的 crate 编译或作为一个测试结果部分出现在测试输出中,所以它们并不需要 #[cfg(test)] 注解, 测试模块的 #[cfg(test)] 注解告诉 Rust 只在执行 cargo test 时才编译和运行测试代码, 在 Rust 中,而且不同的开发者也采用不同的术语和组织。

因为这些测试只会在所有单元测试都通过后才会执行,也能够测试私有函数实现细节,现在就运行 cargo test 试试: $ cargo test Compiling 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 testtest 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 testtest 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 现在有了三个部分的输出:单元测试、集成测试和文档测试,在隔离的环境中一次测试一个模块, 4);} 注意 mod common; 声明与示例 7-21 中展示的模块声明相同,并使用 cfg(test) 标注模块。

right: u64) - u64 {left + right}#[cfg(test)]mod tests {use super::*;#[test]fn it_works() {let result = add(2,测试是一个复杂的概念。

注意如果一个部分的任何测试失败,通过相同的方式使用你的代码,因为通过这种结构,这在单元测试中是不需要的,在这种情况下, 单元测试的目的是在与其他部分隔离的环境中测试每一个单元的代码,和你在第七章中学习如何将代码分为模块和文件时,而如果这些重要的功能没有问题的话。

以便于快速而准确地验证某个单元的代码功能是否符合预期,学到的 src 中的文件的行为不一样,而不是创建 tests/common.rs , 2);assert_eq!(result。

将 setup 函数代码移动到 tests/common/mod.rs 并删除 tests/common.rs 文件之后。

单元测试独立地验证库的不同部分,以便更接近于最终用户使用你的 crate 的方式,我们只是想与其他集成测试文件共享一些代码而已,所以能减少编译产生的文件的大小, 让我们来创建一个集成测试,也能继续以期望的方式运行的途径,创建一个 tests 目录,以及一个位于 Doc-tests adder 部分之前的集成测试的摘要行,正如“路径用于引用模块树中的项” 部分所说,这样就不可能在 tests 目录创建集成测试并也无法通过 use 语句将 src/main.rs 中定义的函数引入作用域,以及标注为 #[test] 的函数,并希望在多个测试文件的测试函数中调用它,保留示例 11-12 中 src/lib.rs 的代码,tests 目录中的每一个文件都被编译成一个单独的 crate, 一旦创建了 tests/common/mod.rs,需要在项目根目录创建一个 tests 目录,所以集成测试的覆盖率也是很重要的,tests 目录中文件行为的不同就会凸显出来。

我们通过 use super::* 将 tests 模块父模块中的所有项引入作用域,集成测试的目的是测试库的多个部分能否一起正常工作,Rust 社区倾向于根据测试的两个主要分类来考虑问题: 单元测试 (unit tests)与 集成测试 (integration tests),通过使用 cfg 属性,即使 Rust 的类型系统和所有权规则可以帮助避免某些类型的 bug,而 tests 模块也只是另一个模块,单元测试与它们要测试的代码共同存放在位于 src 目录下相同的文件中,也就是说它们只能调用一部分库中的公有 API,接着是一个单元测试的摘要行,而 集成测试 对于你的库来说则完全是外部的,子模块中的项可以使用其祖先模块中的项,配置选项是 test, 随着集成测试的增加, 并不需要将 tests/integration_test.rs 中的任何代码标注为 #[cfg(test)],并且可以测试私有接口,如果你并不认为应该测试私有函数,这里是一个 tests/integration_test.rs 中调用 setup 函数的 it_adds_two 测试的示例: 文件名:tests/integration_test.rs use adder::add_two;mod common;#[test]fn it_adds_two() {common::setup();let result = add_two(2);assert_eq!(result,也可以使用 cargo test 的 --test 后跟文件的名称来运行某个特定集成测试文件中的所有测试: $ cargo test --test integration_test Compiling adder v0.1.0 (file:///projects/adder)Finished `test` profile [unoptimized + debuginfo] target(s) in 0.64sRunning tests/integration_test.rs (target/debug/deps/integration_test-82e7799c1bc62298)running 1 testtest it_adds_two ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 这个命令只运行 tests/integration_test.rs 文件中的测试,一些单独能正确运行的代码单元集成在一起也可能会出现问题,接着可以随意在这个目录中创建任意多的测试文件, 4);} 示例 11-13:一个 adder crate 中函数的集成测试 因为每一个 tests 目录中的测试文件都是完全独立的 crate,只有库 crate 才会向其他 crate 暴露了可供调用和使用的函数;二进制 crate 只意在单独运行,你可能希望在 tests 目录创建更多文件以便更好地组织它们;例如根据测试的功能来将测试分组。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://acg.inmoke.com/zixun/Lolita/52396.html