|
|
|
@ -2,7 +2,7 @@
|
|
|
|
|
|
|
|
|
|
> [ch11-03-test-organization.md](https://github.com/rust-lang/book/blob/main/src/ch11-03-test-organization.md)
|
|
|
|
|
> <br>
|
|
|
|
|
> commit cfb2c3cce7c20d4ad523dafdbf90ae3b25b1ba2c
|
|
|
|
|
> commit 654d8902d380dbb8dd94ed2e548dfc0aa80c07cb
|
|
|
|
|
|
|
|
|
|
本章一开始就提到,测试是一个复杂的概念,而且不同的开发者也采用不同的技术和组织。Rust 社区倾向于根据测试的两个主要分类来考虑问题:**单元测试**(*unit tests*)与 **集成测试**(*integration tests*)。单元测试倾向于更小而更集中,在隔离的环境中一次测试一个模块,或者是测试私有接口。而集成测试对于你的库来说则完全是外部的。它们与其他外部代码一样,通过相同的方式使用你的代码,只测试公有接口而且每个测试都有可能会测试多个模块。
|
|
|
|
|
|
|
|
|
@ -48,7 +48,19 @@
|
|
|
|
|
|
|
|
|
|
为了编写集成测试,需要在项目根目录创建一个 *tests* 目录,与 *src* 同级。Cargo 知道如何去寻找这个目录中的集成测试文件。接着可以随意在这个目录中创建任意多的测试文件,Cargo 会将每一个文件当作单独的 crate 来编译。
|
|
|
|
|
|
|
|
|
|
让我们来创建一个集成测试。保留示例 11-12 中 *src/lib.rs* 的代码。创建一个 *tests* 目录,新建一个文件 *tests/integration_test.rs*,并输入示例 11-13 中的代码。
|
|
|
|
|
让我们来创建一个集成测试。保留示例 11-12 中 *src/lib.rs* 的代码。创建一个 *tests* 目录,新建一个文件 *tests/integration_test.rs*。目录结构应该看起来像这样:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
adder
|
|
|
|
|
├── Cargo.lock
|
|
|
|
|
├── Cargo.toml
|
|
|
|
|
├── src
|
|
|
|
|
│ └── lib.rs
|
|
|
|
|
└── tests
|
|
|
|
|
└── integration_test.rs
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
将示例 11-13 中的代码输入到 *tests/integration_test.rs* 文件中。
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:tests/integration_test.rs</span>
|
|
|
|
|
|
|
|
|
@ -58,7 +70,7 @@
|
|
|
|
|
|
|
|
|
|
<span class="caption">示例 11-13:一个 `adder` crate 中函数的集成测试</span>
|
|
|
|
|
|
|
|
|
|
与单元测试不同,我们需要在文件顶部添加 `use adder`。这是因为每一个 `tests` 目录中的测试文件都是完全独立的 crate,所以需要在每一个文件中导入库。
|
|
|
|
|
因为每一个 `tests` 目录中的测试文件都是完全独立的 crate,所以需要在每一个文件中导入库。为此与单元测试不同,我们需要在文件顶部添加 `use adder`。
|
|
|
|
|
|
|
|
|
|
并不需要将 *tests/integration_test.rs* 中的任何代码标注为 `#[cfg(test)]`。 `tests` 文件夹在 Cargo 中是一个特殊的文件夹,Cargo 只会在运行 `cargo test` 时编译这个目录中的文件。现在就运行 `cargo test` 试试:
|
|
|
|
|
|
|
|
|
@ -66,11 +78,13 @@
|
|
|
|
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-13/output.txt}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
现在有了三个部分的输出:单元测试、集成测试和文档测试。第一部分单元测试与我们之前见过的一样:每个单元测试一行(示例 11-12 中有一个叫做 `internal` 的测试),接着是一个单元测试的摘要行。
|
|
|
|
|
现在有了三个部分的输出:单元测试、集成测试和文档测试。注意如果一个部分的任何测试失败,之后的部分都不会运行。例如如果一个单元测试失败,则不会有任何集成测试和文档测试的输出,因为这些测试只会在所有单元测试都通过后才会执行。
|
|
|
|
|
|
|
|
|
|
第一部分单元测试与我们之前见过的一样:每个单元测试一行(示例 11-12 中有一个叫做 `internal` 的测试),接着是一个单元测试的摘要行。
|
|
|
|
|
|
|
|
|
|
集成测试部分以行 `Running target/debug/deps/integration-test-ce99bcc2479f4607`(在输出最后的哈希值可能不同)开头。接下来每一行是一个集成测试中的测试函数,以及一个位于 `Doc-tests adder` 部分之前的集成测试的摘要行。
|
|
|
|
|
集成测试部分以行 `Running tests/integration_test.rs`开头。接下来每一行是一个集成测试中的测试函数,以及一个位于 `Doc-tests adder` 部分之前的集成测试的摘要行。
|
|
|
|
|
|
|
|
|
|
我们已经知道,单元测试函数越多,单元测试部分的结果行就会越多。同样的,在集成文件中增加的测试函数越多,也会在对应的测试结果部分增加越多的结果行。每一个集成测试文件有对应的测试结果部分,所以如果在 *tests* 目录中增加更多文件,测试结果中就会有更多集成测试结果部分。
|
|
|
|
|
每一个集成测试文件有对应的测试结果部分,所以如果在 *tests* 目录中增加更多文件,测试结果中就会有更多集成测试结果部分。
|
|
|
|
|
|
|
|
|
|
我们仍然可以通过指定测试函数的名称作为 `cargo test` 的参数来运行特定集成测试。也可以使用 `cargo test` 的 `--test` 后跟文件的名称来运行某个特定集成测试文件中的所有测试:
|
|
|
|
|
|
|
|
|
@ -86,11 +100,11 @@
|
|
|
|
|
|
|
|
|
|
将每个集成测试文件当作其自己的 crate 来对待,这更有助于创建单独的作用域,这种单独的作用域能提供更类似与最终使用者使用 crate 的环境。然而,正如你在第七章中学习的如何将代码分为模块和文件的知识,*tests* 目录中的文件不能像 *src* 中的文件那样共享相同的行为。
|
|
|
|
|
|
|
|
|
|
当你有一些在多个集成测试文件都会用到的帮助函数,而你尝试按照第七章 “将模块移动到其他文件” 部分的步骤将他们提取到一个通用的模块中时, *tests* 目录中不同文件的行为就会显得很明显。例如,如果我们可以创建 一个*tests/common.rs* 文件并创建一个名叫 `setup` 的函数,我们希望这个函数能被多个测试文件的测试函数调用:
|
|
|
|
|
当你有一些在多个集成测试文件都会用到的帮助函数,而你尝试按照第七章 [“将模块移动到其他文件”][separating-modules-into-files] 部分的步骤将他们提取到一个通用的模块中时, *tests* 目录中不同文件的行为就会显得很明显。例如,如果我们可以创建 一个*tests/common.rs* 文件并创建一个名叫 `setup` 的函数,我们希望这个函数能被多个测试文件的测试函数调用:
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:tests/common.rs</span>
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
```rust,noplayground
|
|
|
|
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/tests/common.rs}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
@ -102,7 +116,20 @@
|
|
|
|
|
|
|
|
|
|
我们并不想要`common` 出现在测试结果中显示 `running 0 tests` 。我们只是希望其能被其他多个集成测试文件中调用罢了。
|
|
|
|
|
|
|
|
|
|
为了不让 `common` 出现在测试输出中,我们将创建 *tests/common/mod.rs* ,而不是创建 *tests/common.rs* 。这是一种 Rust 的命名规范,这样命名告诉 Rust 不要将 `common` 看作一个集成测试文件。将 `setup` 函数代码移动到 *tests/common/mod.rs* 并删除 *tests/common.rs* 文件之后,测试输出中将不会出现这一部分。*tests* 目录中的子目录不会被作为单独的 crate 编译或作为一个测试结果部分出现在测试输出中。
|
|
|
|
|
为了不让 `common` 出现在测试输出中,我们将创建 *tests/common/mod.rs* ,而不是创建 *tests/common.rs* 。现在项目目录结构看起来像这样:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
├── Cargo.lock
|
|
|
|
|
├── Cargo.toml
|
|
|
|
|
├── src
|
|
|
|
|
│ └── lib.rs
|
|
|
|
|
└── tests
|
|
|
|
|
├── common
|
|
|
|
|
│ └── mod.rs
|
|
|
|
|
└── integration_test.rs
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这是一种老的命名规范,正如第七章 [“另一种文件路径”][alt-paths] 中提到的 Rust 仍然理解它们。这样命名告诉 Rust 不要将 `common` 看作一个集成测试文件。将 `setup` 函数代码移动到 *tests/common/mod.rs* 并删除 *tests/common.rs* 文件之后,测试输出中将不会出现这一部分。*tests* 目录中的子目录不会被作为单独的 crate 编译或作为一个测试结果部分出现在测试输出中。
|
|
|
|
|
|
|
|
|
|
一旦拥有了 *tests/common/mod.rs*,就可以将其作为模块以便在任何集成测试文件中使用。这里是一个 *tests/integration_test.rs* 中调用 `setup` 函数的 `it_adds_two` 测试的例子:
|
|
|
|
|
|
|
|
|
@ -129,3 +156,4 @@ Rust 的测试功能提供了一个确保即使你改变了函数的实现方式
|
|
|
|
|
[paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
|
|
|
|
|
[separating-modules-into-files]:
|
|
|
|
|
ch07-05-separating-modules-into-different-files.html
|
|
|
|
|
[alt-paths]: ch07-05-separating-modules-into-different-files.html#alternate-file-paths
|
|
|
|
|