@ -1,8 +1,7 @@
## 如何编写测试
> [ch11-01-writing-tests.md ](https://github.com/rust-lang/book/blob/main/src/ch11-01-writing-tests.md )
> < br >
> commit fcfac818c722cf97f1327f296bbd6fd1d6f2a022
<!-- https://github.com/rust - lang/book/blob/main/src/ch11 - 01 - writing - tests.md -->
<!-- commit 02e053cdbbb3bf9edd9ad32ed49eb533404350a9 -->
Rust 中的测试函数是用来验证非测试代码是否是按照期望的方式运行的。测试函数体通常执行如下三种操作:
@ -28,7 +27,7 @@ $ cargo new adder --lib
$ cd adder
```
adder 库中 _src/lib.rs_ 的内容应该看起来如示例 11-1 所示:
` adder` 库中 _src/lib.rs_ 的内容应该看起来如示例 11-1 所示:
< span class = "filename" > 文件名: src/lib.rs< / span >
@ -38,9 +37,11 @@ adder 库中 _src/lib.rs_ 的内容应该看起来如示例 11-1 所示:
< span class = "caption" > 示例 11-1: 由 `cargo new` 自动生成的测试模块和函数</ span >
文件以一个示例 `add` 函数开头,这样我们就有东西可以测试。
现在让我们只关注 `it_works` 函数本身。注意 `fn` 行之前的 `#[test]` :这个属性表明这是一个测试函数,这样测试执行者就知道将其作为测试处理。`tests` 模块中也可以有非测试的函数来帮助我们建立通用场景或进行常见操作,必须每次都标明哪些函数是测试。
示例函数体通过使用 `assert_eq!` 宏来断言 `result` (其中包含 2 加 2 的结果)等于 4。一个典型的测试的格式, 就是像这个例子中的断言一样 。接下来运行就可以看到测试通过。
示例函数体通过使用 `assert_eq!` 宏来断言 `result` (其中包含 2 加 2 的结果)等于 4。这个断言示例展示了典型测试的格式 。接下来运行就可以看到测试通过。
`cargo test` 命令会运行项目中所有的测试,如示例 11-2 所示:
@ -50,13 +51,13 @@ adder 库中 _src/lib.rs_ 的内容应该看起来如示例 11-1 所示:
< span class = "caption" > 示例 11-2: 运行自动生成测试的输出< / span >
Cargo 编译并运行了测试。可以看到 `running 1 test` 这一行。下一行显示了生成的测试函数的名称,它是 `it_works` ,以及测试的运行结果,`ok`。接着可以看到全体测试运行结果的摘要:`test result: ok.` 意味着所有测试都通过了。`1 passed; 0 failed` 表示通过或失败的测试数量。
Cargo 编译并运行了测试。可以看到 `running 1 test` 这一行。下一行显示了生成的测试函数的名称,`tests:: it_works`,以及测试的运行结果,`ok`。接着可以看到全体测试运行结果的摘要:`test result: ok.` 意味着所有测试都通过了。`1 passed; 0 failed` 表示通过或失败的测试数量。
可以将一个测试标记为忽略这样 在特定情况下它就不会运行;本章之后的[“除非特别指定否则忽略某些测试”][ignoring]部分会介绍它。因为之前我们并没有将任何测试标记为忽略,所以摘要中会显示 `0 ignored` 。
可以将一个测试标记为忽略以便 在特定情况下它就不会运行;本章之后的[“除非特别指定否则忽略某些测试”][ignoring]部分会介绍它。因为之前我们并没有将任何测试标记为忽略,所以摘要中会显示 `0 ignored` 。
`0 measured` 统计是针对性能测试的。性能测试( benchmark tests) 在编写本书时, 仍只能用于 Rust 开发版( nightly Rust) 。请查看 [性能测试的文档][bench] 了解更多。
我们可以将参数传递给 `cargo test` 命令,以便只运行名称与字符串匹配的测试;这就是所谓的 _过滤_ ( _filtering_) , 我们会在 [“通过指定名字来运行部分测试”][subset] 讨论这一点。这里我们没有过滤需要运行的测试,所以摘要中会显示`0 filtered out`。
我们可以将参数传递给 `cargo test` 命令,以便只运行名称与字符串匹配的测试;这就是所谓的**过滤** ( _filtering_) , 我们会在 [“通过指定名字来运行部分测试”][subset] 讨论这一点。这里我们没有过滤需要运行的测试,所以摘要中会显示`0 filtered out`。
测试输出中的以 `Doc-tests adder` 开头的这一部分是所有文档测试的结果。我们现在并没有任何文档测试,不过 Rust 会编译任何在 API 文档中的代码示例。这个功能帮助我们使文档和代码保持同步!在第十四章的 [“文档注释作为测试”][doc-comments] 部分会讲到如何编写文档测试。现在我们将忽略 `Doc-tests` 部分的输出。
@ -144,23 +145,23 @@ Cargo 编译并运行了测试。可以看到 `running 1 test` 这一行。下
{{#include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/output.txt}}
```
两个通过的 测试!现在让我们看看如果引入一个 bug 的话测试结果会发生什么。将 `can_hold` 方法中比较宽度时本应使用大于号的地方改成小于号:
两个测试通过了 !现在让我们看看如果引入一个 bug 的话测试结果会发生什么。将 `can_hold` 方法中比较宽度时本应使用大于号的地方改成小于号:
```rust,not_desired_behavior,noplayground
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/src/lib.rs:here}}
```
现在运行测试会产生:
现在运行测试会产生以下结果 :
```console
{{#include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt}}
```
我们的测试捕获了 bug! 因为 `larger.width` 是 8 而 `smaller.width` 是 5, `can_hold` 中的宽度比较现在因为 8 不小于 5 而返回 `false` 。
我们的测试捕获了 bug! 因为 `larger.width` 是 8 而 `smaller.width` 是 5, `can_hold` 中的宽度比较现在因为 8 不小于 5 而返回 `false` : 8 并不小于 5 。
### 使用 `assert_eq!` 和 `assert_ne!` 宏来 测试相等
### 使用 `assert_eq!` 和 `assert_ne!` 宏测试相等
测试功能的一个常用方法是将需要测试代码的值与期望值做比较,并检查是否相等。可以通过向 `assert!` 宏传递一个使用 `==` 运算符的表达式来做到。不过这个操作实在是太常见了,以至于标准库提供了一对宏来更方便的处理这些操作 —— `assert_eq!` 和 `assert_ne!` 。这两个宏分别比较两个值是相等还是不相等。当断言失败时它们也会打印出这两个值具体是什么,以便于观察测试 _为什么_ 失败,而 `assert!` 只会打印出它从 `==` 表达式中得到了 `false` 值,而不是打印导致 `false` 的两个 值。
测试功能的一个常用方法是将需要测试代码的值与期望值做比较,并检查是否相等。可以通过向 `assert!` 宏传递一个使用 `==` 运算符的表达式来做到。不过这个操作实在是太常见了,以至于标准库提供了一对宏来更方便的处理这些操作 —— `assert_eq!` 和 `assert_ne!` 。这两个宏分别比较两个值是相等还是不相等。当断言失败时它们也会打印出这两个值具体是什么,以便于观察测试**为什么** 失败,而 `assert!` 只会打印出它从 `==` 表达式中得到了 `false` 值,而不是打印导致 `false` 的具体 值。
示例 11-7 中,让我们编写一个对其参数加二并返回结果的函数 `add_two` 。接着使用 `assert_eq!` 宏测试这个函数。
@ -192,11 +193,11 @@ Cargo 编译并运行了测试。可以看到 `running 1 test` 这一行。下
{{#include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt}}
```
测试捕获到了 bug! `it_adds_two` 测试失败,错误信息告诉我们断言失败了,它告诉我们 `` assertion failed: ` ( left == right) ` `` 以及 `left` 和 `right` 的值是什么。这个错误信息有助于我们开始调试:它说 `assert_eq!` 的 `left` 参数(也就是 `add_two(2)` 的结果)是 `5` ,而 `right` 参数是 `4` 。可以想象当有很多测试在运行时这些信息是多么的有用。
测试捕获到了 bug! `it_adds_two` 测试失败,错误信息告诉我们断言失败了,它告诉我们 ``assertion ` left == right` failed `` 以及 `left` 和 `right` 的值是什么。这个错误信息有助于我们开始调试:它说 `assert_eq!` 的 `left` 参数(也就是 `add_two(2)` 的结果)是 `5` ,而 `right` 参数是 `4` 。可以想象当有很多测试在运行时这些信息是多么的有用。
需要注意的是,在一些语言和测试框架中,断言两个值相等的函数的参数被称为 `expected` 和 `actual` ,而且指定参数的顺序非常重要。然而在 Rust 中,它们则叫做 `left` 和 `right` ,同时指定期望的值和被测试代码产生的值的顺序并不重要。这个测试中的断言也可以写成 `assert_eq!( 4 , result)`,这时失败信息仍同样是 `` assertion failed: `(left == right)` ``。
需要注意的是,在一些语言和测试框架中,断言两个值相等的函数的参数被称为 `expected` 和 `actual` ,而且指定参数的顺序非常重要。然而在 Rust 中,它们则叫做 `left` 和 `right` ,同时指定期望的值和被测试代码产生的值的顺序并不重要。这个测试中的断言也可以写成 `assert_eq!( add_two(2) , result)`,这时失败信息仍同样是 `` assertion failed: `(left == right)` ``。
`assert_ne!` 宏在传递给它的两个值不相等时通过,而在相等时失败。当我们不确定值 _会_ 是什么,不过能确定值绝对 _不会_ 是什么的时候,这个宏最有用处。例如,如果一个函数保证会以某种方式改变其输入,不过这种改变方式是由运行测试时是星期几来决定的,这时最好的断言可能就是函数的输出不等于其输入。
`assert_ne!` 宏在传递给它的两个值不相等时通过,而在相等时失败。当我们不确定值**会**是什么,不过能确定值绝对**不会**_ ** 是什么的时候,这个宏最有用处。例如,如果一个函数保证会以某种方式改变其输入,不过这种改变方式是由运行测试时是星期几来决定的,这时最好的断言可能就是函数的输出不等于其输入。
`assert_eq!` 和 `assert_ne!` 宏在底层分别使用了 `==` 和 `!=` 。当断言失败时,这些宏会使用调试格式打印出其参数,这意味着被比较的值必须实现了 `PartialEq` 和 `Debug` trait。所有的基本类型和大部分标准库类型都实现了这些 trait。对于自定义的结构体和枚举, 需要实现 `PartialEq` 才能断言它们的值是否相等。需要实现 `Debug` 才能在断言失败时打印它们的值。因为这两个 trait 都是派生 trait, 如第五章示例 5-12 所提到的,通常可以直接在结构体或枚举上添加 `#[derive(PartialEq, Debug)]` 注解。附录 C [“可派生 trait”][derivable-traits] 中有更多关于这些和其他派生 trait 的详细信息。