mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-14 04:41:49 +08:00
update to ch11-03
This commit is contained in:
parent
eaec47a9ed
commit
7e44e87a77
@ -6,5 +6,5 @@ fn it_works() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn expensive_test() {
|
fn expensive_test() {
|
||||||
// code that takes an hour to run
|
// 需要运行一个小时的代码
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch11-01-writing-tests.md](https://github.com/rust-lang/book/blob/main/src/ch11-01-writing-tests.md)
|
> [ch11-01-writing-tests.md](https://github.com/rust-lang/book/blob/main/src/ch11-01-writing-tests.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit cc6a1ef2614aa94003566027b285b249ccf961fa
|
> commit b9a473ff80e72ed9a77f97a80799b5aff25b594a
|
||||||
|
|
||||||
Rust 中的测试函数是用来验证非测试代码是否按照期望的方式运行的。测试函数体通常执行如下三种操作:
|
Rust 中的测试函数是用来验证非测试代码是否按照期望的方式运行的。测试函数体通常执行如下三种操作:
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ Rust 中的测试函数是用来验证非测试代码是否按照期望的方式
|
|||||||
|
|
||||||
让我们创建一个新的库项目 `adder`:
|
让我们创建一个新的库项目 `adder`:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo new adder --lib
|
$ cargo new adder --lib
|
||||||
Created library `adder` project
|
Created library `adder` project
|
||||||
$ cd adder
|
$ cd adder
|
||||||
@ -32,15 +32,8 @@ adder 库中 `src/lib.rs` 的内容应该看起来如示例 11-1 所示:
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}}
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-1:由 `cargo new` 自动生成的测试模块和函数</span>
|
<span class="caption">示例 11-1:由 `cargo new` 自动生成的测试模块和函数</span>
|
||||||
@ -51,22 +44,8 @@ mod tests {
|
|||||||
|
|
||||||
`cargo test` 命令会运行项目中所有的测试,如示例 11-2 所示:
|
`cargo test` 命令会运行项目中所有的测试,如示例 11-2 所示:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-01/output.txt}}
|
||||||
Compiling adder v0.1.0 (file:///projects/adder)
|
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs
|
|
||||||
Running target/debug/deps/adder-ce99bcc2479f4607
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test tests::it_works ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
Doc-tests adder
|
|
||||||
|
|
||||||
running 0 tests
|
|
||||||
|
|
||||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-2:运行自动生成测试的输出</span>
|
<span class="caption">示例 11-2:运行自动生成测试的输出</span>
|
||||||
@ -85,67 +64,30 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/src/lib.rs}}
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn exploration() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
并再次运行 `cargo test`。现在输出中将出现 `exploration` 而不是 `it_works`:
|
并再次运行 `cargo test`。现在输出中将出现 `exploration` 而不是 `it_works`:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/output.txt}}
|
||||||
test tests::exploration ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
让我们增加另一个测试,不过这一次是一个会失败的测试!当测试函数中出现 panic 时测试就失败了。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败。第九章讲到了最简单的造成 panic 的方法:调用 `panic!` 宏。写入新测试 `another` 后, `src/lib.rs` 现在看起来如示例 11-3 所示:
|
让我们增加另一个测试,不过这一次是一个会失败的测试!当测试函数中出现 panic 时测试就失败了。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败。第九章讲到了最简单的造成 panic 的方法:调用 `panic!` 宏。写入新测试 `another` 后, `src/lib.rs` 现在看起来如示例 11-3 所示:
|
||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust,panics
|
```rust,panics,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-03/src/lib.rs:here}}
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn exploration() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn another() {
|
|
||||||
panic!("Make this test fail");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-3:增加第二个因调用了 `panic!` 而失败的测试</span>
|
<span class="caption">示例 11-3:增加第二个因调用了 `panic!` 而失败的测试</span>
|
||||||
|
|
||||||
再次 `cargo test` 运行测试。输出应该看起来像示例 11-4,它表明 `exploration` 测试通过了而 `another` 失败了:
|
再次 `cargo test` 运行测试。输出应该看起来像示例 11-4,它表明 `exploration` 测试通过了而 `another` 失败了:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 2 tests
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-03/output.txt}}
|
||||||
test tests::exploration ... ok
|
|
||||||
test tests::another ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
---- tests::another stdout ----
|
|
||||||
thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::another
|
|
||||||
|
|
||||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
error: test failed
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-4:一个测试通过和一个测试失败的测试结果</span>
|
<span class="caption">示例 11-4:一个测试通过和一个测试失败的测试结果</span>
|
||||||
@ -164,19 +106,8 @@ error: test failed
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-05/src/lib.rs:here}}
|
||||||
#[derive(Debug)]
|
|
||||||
struct Rectangle {
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rectangle {
|
|
||||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
|
||||||
self.width > other.width && self.height > other.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-5:第五章中 `Rectangle` 结构体和其 `can_hold` 方法</span>
|
<span class="caption">示例 11-5:第五章中 `Rectangle` 结构体和其 `can_hold` 方法</span>
|
||||||
@ -185,20 +116,8 @@ impl Rectangle {
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-06/src/lib.rs:here}}
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn larger_can_hold_smaller() {
|
|
||||||
let larger = Rectangle { width: 8, height: 7 };
|
|
||||||
let smaller = Rectangle { width: 5, height: 1 };
|
|
||||||
|
|
||||||
assert!(larger.can_hold(&smaller));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-6:一个 `can_hold` 的测试,检查一个较大的矩形确实能放得下一个较小的矩形</span>
|
<span class="caption">示例 11-6:一个 `can_hold` 的测试,检查一个较大的矩形确实能放得下一个较小的矩形</span>
|
||||||
@ -207,84 +126,34 @@ mod tests {
|
|||||||
|
|
||||||
我们将测试命名为 `larger_can_hold_smaller`,并创建所需的两个 `Rectangle` 实例。接着调用 `assert!` 宏并传递 `larger.can_hold(&smaller)` 调用的结果作为参数。这个表达式预期会返回 `true`,所以测试应该通过。让我们拭目以待!
|
我们将测试命名为 `larger_can_hold_smaller`,并创建所需的两个 `Rectangle` 实例。接着调用 `assert!` 宏并传递 `larger.can_hold(&smaller)` 调用的结果作为参数。这个表达式预期会返回 `true`,所以测试应该通过。让我们拭目以待!
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-06/output.txt}}
|
||||||
test tests::larger_can_hold_smaller ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
它确实通过了!再来增加另一个测试,这一回断言一个更小的矩形不能放下一个更大的矩形:
|
它确实通过了!再来增加另一个测试,这一回断言一个更小的矩形不能放下一个更大的矩形:
|
||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/src/lib.rs:here}}
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn larger_can_hold_smaller() {
|
|
||||||
// --snip--
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smaller_cannot_hold_larger() {
|
|
||||||
let larger = Rectangle { width: 8, height: 7 };
|
|
||||||
let smaller = Rectangle { width: 5, height: 1 };
|
|
||||||
|
|
||||||
assert!(!smaller.can_hold(&larger));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
因为这里 `can_hold` 函数的正确结果是 `false` ,我们需要将这个结果取反后传递给 `assert!` 宏。因此 `can_hold` 返回 `false` 时测试就会通过:
|
因为这里 `can_hold` 函数的正确结果是 `false` ,我们需要将这个结果取反后传递给 `assert!` 宏。因此 `can_hold` 返回 `false` 时测试就会通过:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 2 tests
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/output.txt}}
|
||||||
test tests::smaller_cannot_hold_larger ... ok
|
|
||||||
test tests::larger_can_hold_smaller ... ok
|
|
||||||
|
|
||||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
两个通过的测试!现在让我们看看如果引入一个 bug 的话测试结果会发生什么。将 `can_hold` 方法中比较长度时本应使用大于号的地方改成小于号:
|
两个通过的测试!现在让我们看看如果引入一个 bug 的话测试结果会发生什么。将 `can_hold` 方法中比较长度时本应使用大于号的地方改成小于号:
|
||||||
|
|
||||||
```rust,not_desired_behavior
|
```rust,not_desired_behavior,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/src/lib.rs:here}}
|
||||||
# #[derive(Debug)]
|
|
||||||
# struct Rectangle {
|
|
||||||
# width: u32,
|
|
||||||
# height: u32,
|
|
||||||
# }
|
|
||||||
// --snip--
|
|
||||||
|
|
||||||
impl Rectangle {
|
|
||||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
|
||||||
self.width < other.width && self.height > other.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
现在运行测试会产生:
|
现在运行测试会产生:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 2 tests
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt}}
|
||||||
test tests::smaller_cannot_hold_larger ... ok
|
|
||||||
test tests::larger_can_hold_smaller ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
---- tests::larger_can_hold_smaller stdout ----
|
|
||||||
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
|
|
||||||
larger.can_hold(&smaller)', src/lib.rs:22:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::larger_can_hold_smaller
|
|
||||||
|
|
||||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
我们的测试捕获了 bug!因为 `larger.length` 是 8 而 `smaller.length` 是 5,`can_hold` 中的长度比较现在因为 8 不小于 5 而返回 `false`。
|
我们的测试捕获了 bug!因为 `larger.length` 是 8 而 `smaller.length` 是 5,`can_hold` 中的长度比较现在因为 8 不小于 5 而返回 `false`。
|
||||||
@ -297,63 +166,30 @@ test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-07/src/lib.rs}}
|
||||||
pub fn add_two(a: i32) -> i32 {
|
|
||||||
a + 2
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_adds_two() {
|
|
||||||
assert_eq!(4, add_two(2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-7:使用 `assert_eq!` 宏测试 `add_two` 函数</span>
|
<span class="caption">示例 11-7:使用 `assert_eq!` 宏测试 `add_two` 函数</span>
|
||||||
|
|
||||||
测试通过了!
|
测试通过了!
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-07/output.txt}}
|
||||||
test tests::it_adds_two ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
传递给 `assert_eq!` 宏的第一个参数 `4` ,等于调用 `add_two(2)` 的结果。测试中的这一行 `test tests::it_adds_two ... ok` 中 `ok` 表明测试通过!
|
传递给 `assert_eq!` 宏的第一个参数 `4` ,等于调用 `add_two(2)` 的结果。测试中的这一行 `test tests::it_adds_two ... ok` 中 `ok` 表明测试通过!
|
||||||
|
|
||||||
在代码中引入一个 bug 来看看使用 `assert_eq!` 的测试失败是什么样的。修改 `add_two` 函数的实现使其加 3:
|
在代码中引入一个 bug 来看看使用 `assert_eq!` 的测试失败是什么样的。修改 `add_two` 函数的实现使其加 3:
|
||||||
|
|
||||||
```rust,not_desired_behavior
|
```rust,not_desired_behavior,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/src/lib.rs:here}}
|
||||||
pub fn add_two(a: i32) -> i32 {
|
|
||||||
a + 3
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
再次运行测试:
|
再次运行测试:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt}}
|
||||||
test tests::it_adds_two ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
---- tests::it_adds_two stdout ----
|
|
||||||
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
|
|
||||||
left: `4`,
|
|
||||||
right: `5`', src/lib.rs:11:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::it_adds_two
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
测试捕获到了 bug!`it_adds_two` 测试失败,显示信息 `` assertion failed: `(left == right)` `` 并表明 `left` 是 `4` 而 `right` 是 `5`。这个信息有助于我们开始调试:它说 `assert_eq!` 的 `left` 参数是 `4`,而 `right` 参数,也就是 `add_two(2)` 的结果,是 `5`。
|
测试捕获到了 bug!`it_adds_two` 测试失败,显示信息 `` assertion failed: `(left == right)` `` 并表明 `left` 是 `4` 而 `right` 是 `5`。这个信息有助于我们开始调试:它说 `assert_eq!` 的 `left` 参数是 `4`,而 `right` 参数,也就是 `add_two(2)` 的结果,是 `5`。
|
||||||
@ -372,72 +208,34 @@ test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-05-greeter/src/lib.rs}}
|
||||||
pub fn greeting(name: &str) -> String {
|
|
||||||
format!("Hello {}!", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn greeting_contains_name() {
|
|
||||||
let result = greeting("Carol");
|
|
||||||
assert!(result.contains("Carol"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
这个程序的需求还没有被确定,因此问候文本开头的 `Hello` 文本很可能会改变。然而我们并不想在需求改变时不得不更新测试,所以相比检查 `greeting` 函数返回的确切值,我们将仅仅断言输出的文本中包含输入参数。
|
这个程序的需求还没有被确定,因此问候文本开头的 `Hello` 文本很可能会改变。然而我们并不想在需求改变时不得不更新测试,所以相比检查 `greeting` 函数返回的确切值,我们将仅仅断言输出的文本中包含输入参数。
|
||||||
|
|
||||||
让我们通过将 `greeting` 改为不包含 `name` 来在代码中引入一个 bug 来测试失败时是怎样的:
|
让我们通过将 `greeting` 改为不包含 `name` 来在代码中引入一个 bug 来测试失败时是怎样的:
|
||||||
|
|
||||||
```rust,not_desired_behavior
|
```rust,not_desired_behavior,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/src/lib.rs:here}}
|
||||||
pub fn greeting(name: &str) -> String {
|
|
||||||
String::from("Hello!")
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
运行测试会产生:
|
运行测试会产生:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt}}
|
||||||
test tests::greeting_contains_name ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
---- tests::greeting_contains_name stdout ----
|
|
||||||
thread 'tests::greeting_contains_name' panicked at 'assertion failed:
|
|
||||||
result.contains("Carol")', src/lib.rs:12:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::greeting_contains_name
|
|
||||||
```
|
```
|
||||||
|
|
||||||
结果仅仅告诉了我们断言失败了和失败的行号。一个更有用的失败信息应该打印出 `greeting` 函数的值。让我们为测试函数增加一个自定义失败信息参数:带占位符的格式字符串,以及 `greeting` 函数的值:
|
结果仅仅告诉了我们断言失败了和失败的行号。一个更有用的失败信息应该打印出 `greeting` 函数的值。让我们为测试函数增加一个自定义失败信息参数:带占位符的格式字符串,以及 `greeting` 函数的值:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
#[test]
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/src/lib.rs:here}}
|
||||||
fn greeting_contains_name() {
|
|
||||||
let result = greeting("Carol");
|
|
||||||
assert!(
|
|
||||||
result.contains("Carol"),
|
|
||||||
"Greeting did not contain name, value was `{}`", result
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
现在如果再次运行测试,将会看到更有价值的信息:
|
现在如果再次运行测试,将会看到更有价值的信息:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
---- tests::greeting_contains_name stdout ----
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt}}
|
||||||
thread 'tests::greeting_contains_name' panicked at 'Greeting did not
|
|
||||||
contain name, value was `Hello!`', src/lib.rs:12:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可以在测试输出中看到所取得的确切的值,这会帮助我们理解真正发生了什么,而不是期望发生什么。
|
可以在测试输出中看到所取得的确切的值,这会帮助我们理解真正发生了什么,而不是期望发生什么。
|
||||||
@ -452,82 +250,28 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-08/src/lib.rs}}
|
||||||
pub struct Guess {
|
|
||||||
value: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Guess {
|
|
||||||
pub fn new(value: i32) -> Guess {
|
|
||||||
if value < 1 || value > 100 {
|
|
||||||
panic!("Guess value must be between 1 and 100, got {}.", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Guess {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn greater_than_100() {
|
|
||||||
Guess::new(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-8:测试会造成 `panic!` 的条件</span>
|
<span class="caption">示例 11-8:测试会造成 `panic!` 的条件</span>
|
||||||
|
|
||||||
`#[should_panic]` 属性位于 `#[test]` 之后,对应的测试函数之前。让我们看看测试通过时它是什么样子:
|
`#[should_panic]` 属性位于 `#[test]` 之后,对应的测试函数之前。让我们看看测试通过时它是什么样子:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-08/output.txt}}
|
||||||
test tests::greater_than_100 ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
看起来不错!现在在代码中引入 bug,移除 `new` 函数在值大于 100 时会 panic 的条件:
|
看起来不错!现在在代码中引入 bug,移除 `new` 函数在值大于 100 时会 panic 的条件:
|
||||||
|
|
||||||
```rust,not_desired_behavior
|
```rust,not_desired_behavior,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-08-guess-with-bug/src/lib.rs:here}}
|
||||||
# pub struct Guess {
|
|
||||||
# value: i32,
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
// --snip--
|
|
||||||
|
|
||||||
impl Guess {
|
|
||||||
pub fn new(value: i32) -> Guess {
|
|
||||||
if value < 1 {
|
|
||||||
panic!("Guess value must be between 1 and 100, got {}.", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Guess {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
如果运行示例 11-8 的测试,它会失败:
|
如果运行示例 11-8 的测试,它会失败:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-08-guess-with-bug/output.txt}}
|
||||||
test tests::greater_than_100 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::greater_than_100
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
这回并没有得到非常有用的信息,不过一旦我们观察测试函数,会发现它标注了 `#[should_panic]`。这个错误意味着代码中测试函数 `Guess::new(200)` 并没有产生 panic。
|
这回并没有得到非常有用的信息,不过一旦我们观察测试函数,会发现它标注了 `#[should_panic]`。这个错误意味着代码中测试函数 `Guess::new(200)` 并没有产生 panic。
|
||||||
@ -536,40 +280,8 @@ test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-09/src/lib.rs:here}}
|
||||||
# pub struct Guess {
|
|
||||||
# value: i32,
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
// --snip--
|
|
||||||
|
|
||||||
impl Guess {
|
|
||||||
pub fn new(value: i32) -> Guess {
|
|
||||||
if value < 1 {
|
|
||||||
panic!("Guess value must be greater than or equal to 1, got {}.",
|
|
||||||
value);
|
|
||||||
} else if value > 100 {
|
|
||||||
panic!("Guess value must be less than or equal to 100, got {}.",
|
|
||||||
value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Guess {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Guess value must be less than or equal to 100")]
|
|
||||||
fn greater_than_100() {
|
|
||||||
Guess::new(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-9:一个会带有特定错误信息的 `panic!` 条件的测试</span>
|
<span class="caption">示例 11-9:一个会带有特定错误信息的 `panic!` 条件的测试</span>
|
||||||
@ -579,32 +291,13 @@ mod tests {
|
|||||||
为了观察带有 `expected` 信息的 `should_panic` 测试失败时会发生什么,让我们再次引入一个 bug,将 `if value < 1` 和 `else if value > 100` 的代码块对换:
|
为了观察带有 `expected` 信息的 `should_panic` 测试失败时会发生什么,让我们再次引入一个 bug,将 `if value < 1` 和 `else if value > 100` 的代码块对换:
|
||||||
|
|
||||||
```rust,ignore,not_desired_behavior
|
```rust,ignore,not_desired_behavior
|
||||||
if value < 1 {
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/src/lib.rs:here}}
|
||||||
panic!("Guess value must be less than or equal to 100, got {}.", value);
|
|
||||||
} else if value > 100 {
|
|
||||||
panic!("Guess value must be greater than or equal to 1, got {}.", value);
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
这一次运行 `should_panic` 测试,它会失败:
|
这一次运行 `should_panic` 测试,它会失败:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt}}
|
||||||
test tests::greater_than_100 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
---- tests::greater_than_100 stdout ----
|
|
||||||
thread 'tests::greater_than_100' panicked at 'Guess value must be
|
|
||||||
greater than or equal to 1, got 200.', src/lib.rs:11:13
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
note: Panic did not include expected string 'Guess value must be less than or
|
|
||||||
equal to 100'
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::greater_than_100
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
失败信息表明测试确实如期望 panic 了,不过 panic 信息中并没有包含 `expected` 信息 `'Guess value must be less than or equal to 100'`。而我们得到的 panic 信息是 `'Guess value must be greater than or equal to 1, got 200.'`。这样就可以开始寻找 bug 在哪了!
|
失败信息表明测试确实如期望 panic 了,不过 panic 信息中并没有包含 `expected` 信息 `'Guess value must be less than or equal to 100'`。而我们得到的 panic 信息是 `'Guess value must be greater than or equal to 1, got 200.'`。这样就可以开始寻找 bug 在哪了!
|
||||||
@ -613,32 +306,22 @@ test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
|
|
||||||
目前为止,我们编写的测试在失败时就会 panic。也可以使用 `Result<T, E>` 编写测试!这里是第一个例子采用了 Result:
|
目前为止,我们编写的测试在失败时就会 panic。也可以使用 `Result<T, E>` 编写测试!这里是第一个例子采用了 Result:
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
#[cfg(test)]
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-10-result-in-tests/src/lib.rs}}
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() -> Result<(), String> {
|
|
||||||
if 2 + 2 == 4 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(String::from("two plus two does not equal four"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
现在 `it_works` 函数的返回值类型为 `Result<(), String>`。在函数体中,不同于调用 `assert_eq!` 宏,而是在测试通过时返回 `Ok(())`,在测试失败时返回带有 `String` 的 `Err`。
|
现在 `it_works` 函数的返回值类型为 `Result<(), String>`。在函数体中,不同于调用 `assert_eq!` 宏,而是在测试通过时返回 `Ok(())`,在测试失败时返回带有 `String` 的 `Err`。
|
||||||
|
|
||||||
这样编写测试来返回 `Result<T, E>` 就可以在函数体中使用问号运算符,如此可以方便的编写任何运算符会返回 `Err` 成员的测试。
|
这样编写测试来返回 `Result<T, E>` 就可以在函数体中使用问号运算符,如此可以方便的编写任何运算符会返回 `Err` 成员的测试。
|
||||||
|
|
||||||
不能对这些使用 `Result<T, E>` 的测试使用 `#[should_panic]` 注解。相反应该在测试失败时直接返回 `Err` 值。
|
不能对这些使用 `Result<T, E>` 的测试使用 `#[should_panic]` 注解。为了断言一个操作返回 `Err` 成员,**不要**使用对 `Result<T, E>` 值使用问好表达式(`?`)。而是使用 `assert!(value.is_err())`。
|
||||||
|
|
||||||
现在你知道了几种编写测试的方法,让我们看看运行测试时会发生什么,和可以用于 `cargo test` 的不同选项。
|
现在你知道了几种编写测试的方法,让我们看看运行测试时会发生什么,和可以用于 `cargo test` 的不同选项。
|
||||||
|
|
||||||
[concatenation-with-the--operator-or-the-format-macro]:
|
[concatenation-with-the--operator-or-the-format-macro]:
|
||||||
ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro
|
ch08-02-strings.html#使用--运算符或-format-宏拼接字符串
|
||||||
[controlling-how-tests-are-run]:
|
[controlling-how-tests-are-run]:
|
||||||
ch11-02-running-tests.html#controlling-how-tests-are-run
|
ch11-02-running-tests.html#控制测试如何运行
|
||||||
[derivable-traits]: appendix-03-derivable-traits.html
|
[derivable-traits]: appendix-03-derivable-traits.html
|
||||||
[doc-comments]: ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests
|
[doc-comments]: ch14-02-publishing-to-crates-io.html#文档注释作为测试
|
||||||
[paths-for-referring-to-an-item-in-the-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
|
[paths-for-referring-to-an-item-in-the-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch11-02-running-tests.md](https://github.com/rust-lang/book/blob/main/src/ch11-02-running-tests.md)
|
> [ch11-02-running-tests.md](https://github.com/rust-lang/book/blob/main/src/ch11-02-running-tests.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 42b802f26197f9a066e4a671d2b062af25972c13
|
> commit 1721a106f78c037ca3074d9c2d5a8cf9c9852cf7
|
||||||
|
|
||||||
就像 `cargo run` 会编译代码并运行生成的二进制文件一样,`cargo test` 在测试模式下编译代码并运行生成的测试二进制文件。可以指定命令行参数来改变 `cargo test` 的默认行为。例如,`cargo test` 生成的二进制文件的默认行为是并行的运行所有测试,并截获测试运行过程中产生的输出,阻止他们被显示出来,使得阅读测试结果相关的内容变得更容易。
|
就像 `cargo run` 会编译代码并运行生成的二进制文件一样,`cargo test` 在测试模式下编译代码并运行生成的测试二进制文件。可以指定命令行参数来改变 `cargo test` 的默认行为。例如,`cargo test` 生成的二进制文件的默认行为是并行的运行所有测试,并截获测试运行过程中产生的输出,阻止他们被显示出来,使得阅读测试结果相关的内容变得更容易。
|
||||||
|
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
如果你不希望测试并行运行,或者想要更加精确的控制线程的数量,可以传递 `--test-threads` 参数和希望使用线程的数量给测试二进制文件。例如:
|
如果你不希望测试并行运行,或者想要更加精确的控制线程的数量,可以传递 `--test-threads` 参数和希望使用线程的数量给测试二进制文件。例如:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test -- --test-threads=1
|
$ cargo test -- --test-threads=1
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -30,85 +30,32 @@ $ cargo test -- --test-threads=1
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust,panics
|
```rust,panics,noplayground
|
||||||
fn prints_and_returns_10(a: i32) -> i32 {
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-10/src/lib.rs}}
|
||||||
println!("I got the value {}", a);
|
|
||||||
10
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn this_test_will_pass() {
|
|
||||||
let value = prints_and_returns_10(4);
|
|
||||||
assert_eq!(10, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn this_test_will_fail() {
|
|
||||||
let value = prints_and_returns_10(8);
|
|
||||||
assert_eq!(5, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-10:一个调用了 `println!` 的函数的测试</span>
|
<span class="caption">示例 11-10:一个调用了 `println!` 的函数的测试</span>
|
||||||
|
|
||||||
运行 `cargo test` 将会看到这些测试的输出:
|
运行 `cargo test` 将会看到这些测试的输出:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 2 tests
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-10/output.txt}}
|
||||||
test tests::this_test_will_pass ... ok
|
|
||||||
test tests::this_test_will_fail ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
---- tests::this_test_will_fail stdout ----
|
|
||||||
I got the value 8
|
|
||||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
|
|
||||||
left: `5`,
|
|
||||||
right: `10`', src/lib.rs:19:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::this_test_will_fail
|
|
||||||
|
|
||||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
注意输出中不会出现测试通过时打印的内容,即 `I got the value 4`。因为当测试通过时,这些输出会被截获。失败测试的输出 `I got the value 8` ,则出现在输出的测试摘要部分,同时也显示了测试失败的原因。
|
注意输出中不会出现测试通过时打印的内容,即 `I got the value 4`。因为当测试通过时,这些输出会被截获。失败测试的输出 `I got the value 8` ,则出现在输出的测试摘要部分,同时也显示了测试失败的原因。
|
||||||
|
|
||||||
如果你希望也能看到通过的测试中打印的值,截获输出的行为可以通过 `--nocapture` 参数来禁用:
|
如果你希望也能看到通过的测试中打印的值,也可以在结尾加上 `--show-output` 告诉 Rust 显示成功测试的输出。
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test -- --nocapture
|
$ cargo test -- --show-output
|
||||||
```
|
```
|
||||||
|
|
||||||
使用 `--nocapture` 参数再次运行示例 11-10 中的测试会显示如下输出:
|
使用 `--show-output` 参数再次运行示例 11-10 中的测试会显示如下输出:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 2 tests
|
{{#include ../listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt}}
|
||||||
I got the value 4
|
|
||||||
I got the value 8
|
|
||||||
test tests::this_test_will_pass ... ok
|
|
||||||
thread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`
|
|
||||||
left: `5`,
|
|
||||||
right: `10`', src/lib.rs:19:9
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
test tests::this_test_will_fail ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
tests::this_test_will_fail
|
|
||||||
|
|
||||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
注意测试的输出和测试结果的输出是相互交叉的,这是由于测试是并行运行的(见上一部分)。尝试一同使用 `--test-threads=1` 和 `--nocapture` 功能来看看输出是什么样子!
|
|
||||||
|
|
||||||
### 通过指定名字来运行部分测试
|
### 通过指定名字来运行部分测试
|
||||||
|
|
||||||
有时运行整个测试集会耗费很长时间。如果你负责特定位置的代码,你可能会希望只运行与这些代码相关的测试。你可以向 `cargo test` 传递所希望运行的测试名称的参数来选择运行哪些测试。
|
有时运行整个测试集会耗费很长时间。如果你负责特定位置的代码,你可能会希望只运行与这些代码相关的测试。你可以向 `cargo test` 传递所希望运行的测试名称的参数来选择运行哪些测试。
|
||||||
@ -117,58 +64,24 @@ test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
pub fn add_two(a: i32) -> i32 {
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-11/src/lib.rs}}
|
||||||
a + 2
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_two_and_two() {
|
|
||||||
assert_eq!(4, add_two(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_three_and_two() {
|
|
||||||
assert_eq!(5, add_two(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn one_hundred() {
|
|
||||||
assert_eq!(102, add_two(100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-11:不同名称的三个测试</span>
|
<span class="caption">示例 11-11:不同名称的三个测试</span>
|
||||||
|
|
||||||
如果没有传递任何参数就运行测试,如你所见,所有测试都会并行运行:
|
如果没有传递任何参数就运行测试,如你所见,所有测试都会并行运行:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 3 tests
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-11/output.txt}}
|
||||||
test tests::add_two_and_two ... ok
|
|
||||||
test tests::add_three_and_two ... ok
|
|
||||||
test tests::one_hundred ... ok
|
|
||||||
|
|
||||||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 运行单个测试
|
#### 运行单个测试
|
||||||
|
|
||||||
可以向 `cargo test` 传递任意测试的名称来只运行这个测试:
|
可以向 `cargo test` 传递任意测试的名称来只运行这个测试:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test one_hundred
|
{{#include ../listings/ch11-writing-automated-tests/output-only-02-single-test/output.txt}}
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
|
||||||
Running target/debug/deps/adder-06a75b4a1f2515e9
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test tests::one_hundred ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
只有名称为 `one_hundred` 的测试被运行了;因为其余两个测试并不匹配这个名称。测试输出在摘要行的结尾显示了 `2 filtered out` 表明还存在比本次所运行的测试更多的测试被过滤掉了。
|
只有名称为 `one_hundred` 的测试被运行了;因为其余两个测试并不匹配这个名称。测试输出在摘要行的结尾显示了 `2 filtered out` 表明还存在比本次所运行的测试更多的测试被过滤掉了。
|
||||||
@ -179,16 +92,8 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out
|
|||||||
|
|
||||||
我们可以指定部分测试的名称,任何名称匹配这个名称的测试会被运行。例如,因为头两个测试的名称包含 `add`,可以通过 `cargo test add` 来运行这两个测试:
|
我们可以指定部分测试的名称,任何名称匹配这个名称的测试会被运行。例如,因为头两个测试的名称包含 `add`,可以通过 `cargo test add` 来运行这两个测试:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test add
|
{{#include ../listings/ch11-writing-automated-tests/output-only-03-multiple-tests/output.txt}}
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
|
||||||
Running target/debug/deps/adder-06a75b4a1f2515e9
|
|
||||||
|
|
||||||
running 2 tests
|
|
||||||
test tests::add_two_and_two ... ok
|
|
||||||
test tests::add_three_and_two ... ok
|
|
||||||
|
|
||||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
这运行了所有名字中带有 `add` 的测试,也过滤掉了名为 `one_hundred` 的测试。同时注意测试所在的模块也是测试名称的一部分,所以可以通过模块名来运行一个模块中的所有测试。
|
这运行了所有名字中带有 `add` 的测试,也过滤掉了名为 `one_hundred` 的测试。同时注意测试所在的模块也是测试名称的一部分,所以可以通过模块名来运行一个模块中的所有测试。
|
||||||
@ -199,45 +104,20 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
#[test]
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-11-ignore-a-test/src/lib.rs}}
|
||||||
fn it_works() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn expensive_test() {
|
|
||||||
// 需要运行一个小时的代码
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
对于想要排除的测试,我们在 `#[test]` 之后增加了 `#[ignore]` 行。现在如果运行测试,就会发现 `it_works` 运行了,而 `expensive_test` 没有运行:
|
对于想要排除的测试,我们在 `#[test]` 之后增加了 `#[ignore]` 行。现在如果运行测试,就会发现 `it_works` 运行了,而 `expensive_test` 没有运行:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-11-ignore-a-test/output.txt}}
|
||||||
Compiling adder v0.1.0 (file:///projects/adder)
|
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.24 secs
|
|
||||||
Running target/debug/deps/adder-ce99bcc2479f4607
|
|
||||||
|
|
||||||
running 2 tests
|
|
||||||
test expensive_test ... ignored
|
|
||||||
test it_works ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`expensive_test` 被列为 `ignored`,如果我们只希望运行被忽略的测试,可以使用 `cargo test -- --ignored`:
|
`expensive_test` 被列为 `ignored`,如果我们只希望运行被忽略的测试,可以使用 `cargo test -- --ignored`:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test -- --ignored
|
{{#include ../listings/ch11-writing-automated-tests/output-only-04-running-ignored/output.txt}}
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
|
||||||
Running target/debug/deps/adder-ce99bcc2479f4607
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test expensive_test ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
通过控制运行哪些测试,你可以确保能够快速地运行 `cargo test` 。当你需要运行 `ignored` 的测试时,可以执行 `cargo test -- --ignored`。
|
通过控制运行哪些测试,你可以确保能够快速地运行 `cargo test` 。当你需要运行 `ignored` 的测试时,可以执行 `cargo test -- --ignored`。如果你希望不管是否忽略都要运行全部测试,可以运行 `cargo test -- --include-ignored`。
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch11-03-test-organization.md](https://github.com/rust-lang/book/blob/main/src/ch11-03-test-organization.md)
|
> [ch11-03-test-organization.md](https://github.com/rust-lang/book/blob/main/src/ch11-03-test-organization.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 4badf9a8574c12794795b05954baf5adc579fa90
|
> commit cfb2c3cce7c20d4ad523dafdbf90ae3b25b1ba2c
|
||||||
|
|
||||||
本章一开始就提到,测试是一个复杂的概念,而且不同的开发者也采用不同的技术和组织。Rust 社区倾向于根据测试的两个主要分类来考虑问题:**单元测试**(*unit tests*)与 **集成测试**(*integration tests*)。单元测试倾向于更小而更集中,在隔离的环境中一次测试一个模块,或者是测试私有接口。而集成测试对于你的库来说则完全是外部的。它们与其他外部代码一样,通过相同的方式使用你的代码,只测试公有接口而且每个测试都有可能会测试多个模块。
|
本章一开始就提到,测试是一个复杂的概念,而且不同的开发者也采用不同的技术和组织。Rust 社区倾向于根据测试的两个主要分类来考虑问题:**单元测试**(*unit tests*)与 **集成测试**(*integration tests*)。单元测试倾向于更小而更集中,在隔离的环境中一次测试一个模块,或者是测试私有接口。而集成测试对于你的库来说则完全是外部的。它们与其他外部代码一样,通过相同的方式使用你的代码,只测试公有接口而且每个测试都有可能会测试多个模块。
|
||||||
|
|
||||||
@ -20,14 +20,8 @@
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
#[cfg(test)]
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}}
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上述代码就是自动生成的测试模块。`cfg` 属性代表 *configuration* ,它告诉 Rust 其之后的项只应该被包含进特定配置选项中。在这个例子中,配置选项是 `test`,即 Rust 所提供的用于编译和运行测试的配置选项。通过使用 `cfg` 属性,Cargo 只会在我们主动使用 `cargo test` 运行测试时才编译测试代码。这包括测试模块中可能存在的帮助函数, 以及标注为 #[test] 的函数。
|
上述代码就是自动生成的测试模块。`cfg` 属性代表 *configuration* ,它告诉 Rust 其之后的项只应该被包含进特定配置选项中。在这个例子中,配置选项是 `test`,即 Rust 所提供的用于编译和运行测试的配置选项。通过使用 `cfg` 属性,Cargo 只会在我们主动使用 `cargo test` 运行测试时才编译测试代码。这包括测试模块中可能存在的帮助函数, 以及标注为 #[test] 的函数。
|
||||||
@ -38,31 +32,13 @@ mod tests {
|
|||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust,noplayground
|
||||||
# fn main() {}
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-12/src/lib.rs}}
|
||||||
|
|
||||||
pub fn add_two(a: i32) -> i32 {
|
|
||||||
internal_adder(a, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn internal_adder(a: i32, b: i32) -> i32 {
|
|
||||||
a + b
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn internal() {
|
|
||||||
assert_eq!(4, internal_adder(2, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-12:测试私有函数</span>
|
<span class="caption">示例 11-12:测试私有函数</span>
|
||||||
|
|
||||||
注意 `internal_adder` 函数并没有标记为 `pub`,不过因为测试也不过是 Rust 代码同时 `tests` 也仅仅是另一个模块,我们完全可以在测试中导入和调用 `internal_adder`。如果你并不认为应该测试私有函数,Rust 也不会强迫你这么做。
|
注意 `internal_adder` 函数并没有标记为 `pub`。测试也不过是 Rust 代码,同时 `tests` 也仅仅是另一个模块。正如 [“路径用于引用模块树中的项”][paths] 部分所说,子模块的项可以使用其上级模块的项。在测试中,我们通过 `use super::*` 将 `test` 模块的父模块的所有项引入了作用域,接着测试调用了 `internal_adder`。如果你并不认为应该测试私有函数,Rust 也不会强迫你这么做。
|
||||||
|
|
||||||
### 集成测试
|
### 集成测试
|
||||||
|
|
||||||
@ -77,12 +53,7 @@ mod tests {
|
|||||||
<span class="filename">文件名: tests/integration_test.rs</span>
|
<span class="filename">文件名: tests/integration_test.rs</span>
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use adder;
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-13/tests/integration_test.rs}}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_adds_two() {
|
|
||||||
assert_eq!(4, adder::add_two(2));
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">示例 11-13:一个 `adder` crate 中函数的集成测试</span>
|
<span class="caption">示例 11-13:一个 `adder` crate 中函数的集成测试</span>
|
||||||
@ -91,29 +62,8 @@ fn it_adds_two() {
|
|||||||
|
|
||||||
并不需要将 *tests/integration_test.rs* 中的任何代码标注为 `#[cfg(test)]`。 `tests` 文件夹在 Cargo 中是一个特殊的文件夹, Cargo 只会在运行 `cargo test` 时编译这个目录中的文件。现在就运行 `cargo test` 试试:
|
并不需要将 *tests/integration_test.rs* 中的任何代码标注为 `#[cfg(test)]`。 `tests` 文件夹在 Cargo 中是一个特殊的文件夹, Cargo 只会在运行 `cargo test` 时编译这个目录中的文件。现在就运行 `cargo test` 试试:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test
|
{{#include ../listings/ch11-writing-automated-tests/listing-11-13/output.txt}}
|
||||||
Compiling adder v0.1.0 (file:///projects/adder)
|
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
|
||||||
Running target/debug/deps/adder-abcabcabc
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test tests::internal ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
Running target/debug/deps/integration_test-ce99bcc2479f4607
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test it_adds_two ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
Doc-tests adder
|
|
||||||
|
|
||||||
running 0 tests
|
|
||||||
|
|
||||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
现在有了三个部分的输出:单元测试、集成测试和文档测试。第一部分单元测试与我们之前见过的一样:每个单元测试一行(示例 11-12 中有一个叫做 `internal` 的测试),接着是一个单元测试的摘要行。
|
现在有了三个部分的输出:单元测试、集成测试和文档测试。第一部分单元测试与我们之前见过的一样:每个单元测试一行(示例 11-12 中有一个叫做 `internal` 的测试),接着是一个单元测试的摘要行。
|
||||||
@ -124,15 +74,8 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
|
|
||||||
我们仍然可以通过指定测试函数的名称作为 `cargo test` 的参数来运行特定集成测试。也可以使用 `cargo test` 的 `--test` 后跟文件的名称来运行某个特定集成测试文件中的所有测试:
|
我们仍然可以通过指定测试函数的名称作为 `cargo test` 的参数来运行特定集成测试。也可以使用 `cargo test` 的 `--test` 后跟文件的名称来运行某个特定集成测试文件中的所有测试:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
$ cargo test --test integration_test
|
{{#include ../listings/ch11-writing-automated-tests/output-only-05-single-integration/output.txt}}
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
|
|
||||||
Running target/debug/integration_test-952a27e0126bb565
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test it_adds_two ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
这个命令只运行了 *tests* 目录中我们指定的文件 `integration_test.rs` 中的测试。
|
这个命令只运行了 *tests* 目录中我们指定的文件 `integration_test.rs` 中的测试。
|
||||||
@ -148,37 +91,13 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
<span class="filename">文件名: tests/common.rs</span>
|
<span class="filename">文件名: tests/common.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn setup() {
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/tests/common.rs}}
|
||||||
// 编写特定库测试所需的代码
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
如果再次运行测试,将会在测试结果中看到一个新的对应 *common.rs* 文件的测试结果部分,即便这个文件并没有包含任何测试函数,也没有任何地方调用了 `setup` 函数:
|
如果再次运行测试,将会在测试结果中看到一个新的对应 *common.rs* 文件的测试结果部分,即便这个文件并没有包含任何测试函数,也没有任何地方调用了 `setup` 函数:
|
||||||
|
|
||||||
```text
|
```console
|
||||||
running 1 test
|
{{#include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/output.txt}}
|
||||||
test tests::internal ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
Running target/debug/deps/common-b8b07b6f1be2db70
|
|
||||||
|
|
||||||
running 0 tests
|
|
||||||
|
|
||||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
Running target/debug/deps/integration_test-d993c68b431d39df
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
test it_adds_two ... ok
|
|
||||||
|
|
||||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
|
|
||||||
Doc-tests adder
|
|
||||||
|
|
||||||
running 0 tests
|
|
||||||
|
|
||||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|
||||||
```
|
```
|
||||||
|
|
||||||
我们并不想要`common` 出现在测试结果中显示 `running 0 tests` 。我们只是希望其能被其他多个集成测试文件中调用罢了。
|
我们并不想要`common` 出现在测试结果中显示 `running 0 tests` 。我们只是希望其能被其他多个集成测试文件中调用罢了。
|
||||||
@ -190,18 +109,10 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
|||||||
<span class="filename">文件名: tests/integration_test.rs</span>
|
<span class="filename">文件名: tests/integration_test.rs</span>
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use adder;
|
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-13-fix-shared-test-code-problem/tests/integration_test.rs}}
|
||||||
|
|
||||||
mod common;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_adds_two() {
|
|
||||||
common::setup();
|
|
||||||
assert_eq!(4, adder::add_two(2));
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
注意 `mod common;` 声明与示例 7-25 中展示的模块声明相同。接着在测试函数中就可以调用 `common::setup()` 了。
|
注意 `mod common;` 声明与示例 7-21 中展示的模块声明相同。接着在测试函数中就可以调用 `common::setup()` 了。
|
||||||
|
|
||||||
#### 二进制 crate 的集成测试
|
#### 二进制 crate 的集成测试
|
||||||
|
|
||||||
@ -215,5 +126,6 @@ Rust 的测试功能提供了一个确保即使你改变了函数的实现方式
|
|||||||
|
|
||||||
让我们将本章和其他之前章节所学的知识组合起来,在下一章一起编写一个项目!
|
让我们将本章和其他之前章节所学的知识组合起来,在下一章一起编写一个项目!
|
||||||
|
|
||||||
|
[paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
|
||||||
[separating-modules-into-files]:
|
[separating-modules-into-files]:
|
||||||
ch07-05-separating-modules-into-different-files.html
|
ch07-05-separating-modules-into-different-files.html
|
||||||
|
Loading…
Reference in New Issue
Block a user