## Test Organization ​ Rust社区认为测试主要分为两类:单元测试和集成测试。单元测试较小且更集中,每次单独测试**一个模块**,并且可以测试**私有接口**。集成测试则完全在库外部,并且与任何其他外部代码相同的方式使用代码,只使用**公共接口**,并且可能在每个测试中使用**多个模块**。 ### 单元测试 ​ 单元测试的目的是在与其他代码**隔离**的情况下测试每个代码单元。传统做法是在每个文件中创建名为**tests的模块**来包含测试函数,并用`cfg(test)`注释该模块。 ​ 在测试模块上的 `#[cfg(test)]` 注解告诉 Rust 仅在运行 `cargo test` 时编译和运行测试代码,而不在运行 `cargo build` 时编译。而因为集成测试在不同的目录中,它们不需要 `#[cfg(test)]` 注解。 cfg属性代表configuration,它告诉Rust只有在给定某个配置选项时才应该包含下面的项,在本例中,配置选项是test。 `cfg` 属性可以使用多个参数指定编译条件,其中一些常见的参数如下: - `test`:仅在运行测试时编译 - `debug_assertions`:仅在使用 `cargo build` 或 `cargo run` 命令的 `--debug` 模式下编译 - `unix` 或 `windows`:仅在目标操作系统为 Unix 或 Windows 时编译 - `target_arch` 和 `target_os`:仅在目标 CPU 架构和操作系统匹配时编译 #### 测试私有函数 ![image-20230310160912780](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310160912780.png) ### 集成测试 ​ 集成测试只能调用属于您库的公共 API 的函数。目的是测试您的库的许多部分是否可以正确协同工作。不需要添加**\#[cfg(test)]**,Cargo对tests目录进行了特殊处理,仅在运行Cargo测试时才编译该目录中的文件。 #### 测试目录 ​ 测试目录在项目目录顶层,紧挨着src。目录名称必须为tests。 ``` adder ├── Cargo.lock ├── Cargo.toml ├── src │   └── lib.rs └── tests └── integration_test.rs ``` ​ 如果单元测试失败,则不会有任何集成和文档测试的输出,因为这些测试只有在所有单元测试都通过时才会运行。按顺序来的,如果集成测试出错,那么不会有文档测试输出。 ​ 运行测试无误时结果如下: ![image-20230310163621820](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310163621820.png) ​ ​ 集成测试也可以指定名称来运行: ![image-20230310165323146](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310165323146.png) ​ 运行特定集成测试文件中的所有测试,`cargo test --test 完整文件名` ![image-20230310164959908](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310164959908.png) #### 集成测试中子模块 ​ 集成测试可以根据它们测试的功能将测试函数分组。同时当存在多个集成测试文件时,可以提取出相同的行为放入公共模块。 ##### 集成测试中添加通用模块: ​ 错误位置: ![image-20230310171134280](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310171134280.png) ​ ​ 正确位置: ![image-20230310171717119](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310171717119.png) ​ 运行图(不含show-output): ![image-20230310171944280](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230310171944280.png) ​ 使用旧的 Rust 命名约定,即文件名为 `mod.rs` 的文件被视为包含该目录的模块的**主要文件**,而不是一个单独的集成测试文件。因此,Rust **不会将其视为一个集成测试文件**。这种约定在较旧的 Rust 代码中很常见,因此 Rust 保留了对这种方式的支持,以保持向后兼容性。 ​ 项目目录: ``` ├── Cargo.lock ├── Cargo.toml ├── src │   └── lib.rs └── tests ├── common │   └── mod.rs └── integration_test.rs ``` ### 针对二进制包的集成测试 ​ 如果项目中只包含一个 `src/main.rs` 文件而没有 `src/lib.rs` 文件,我们无法在 `tests` 目录中创建集成测试,并使用 `use` 语句将 `src/main.rs` 中定义的函数引入作用域。这是因为**只有库箱(library crate)才会公开其他箱可以使用的函数,而二进制箱本身可以单独运行**。因此,Rust 项目通常将**重要逻辑**放在 `src/lib.rs` 文件中,并在 `src/main.rs` 文件中**调用**这些逻辑,以此为基础来构建二进制箱。 ​ **二进制箱可以进行单元测试**。在二进制箱中,可以将测试放在 `src/main.rs` 文件的底部,就像在库中将测试放在与源码文件相同的文件中一样。您可以使用 `#[cfg(test)]` 属性来标记测试函数,以便它们只在运行 `cargo test` 命令时进行编译和运行。请注意,因为二进制箱的目的是作为独立的可执行文件运行,而不是作为库被其他crate使用,因此在二进制箱中编写的测试通常会更注重对**集成测试的覆盖**,而不是对库代码进行单元测试。 ​ 单元测试: ![image-20230312135712101](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230312135712101.png) ​ ​ 集成测试(失败): ![image-20230312140138211](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230312140138211.png) ​ ​ 集成测试加上lib(成功): ![image-20230312140255410](C:\Users\10074\AppData\Roaming\Typora\typora-user-images\image-20230312140255410.png)