运行测试

ch11-02-running-tests.md
commit cf52d81371e24e14ce31a5582bfcb8c5b80d26cc

类似于cargo run会编译代码并运行生成的二进制文件,cargo test在测试模式下编译代码并运行生成的测试二进制文件。cargo test生成的二进制文件默认会并行的运行所有测试并在测试过程中捕获生成的输出,这样就更容易阅读测试结果的输出。

可以通过指定命令行选项来改变这些运行测试的默认行为。这些选项的一部分可以传递给cargo test,而另一些则需要传递给生成的测试二进制文件。分隔这些参数的方法是--cargo test之后列出了传递给cargo test的参数,接着是分隔符--,之后是传递给测试二进制文件的参数。

并行运行测试

测试使用线程来并行运行。为此,编写测试时需要注意测试之间不要相互依赖或者存在任何共享状态。共享状态也可能包含在运行环境中,比如当前工作目录或者环境变量。

如果你不希望它这样运行,或者想要更加精确的控制使用线程的数量,可以传递--test-threads参数和线程的数量给测试二进制文件。将线程数设置为 1 意味着没有任何并行操作:

$ cargo test -- --test-threads=1

捕获测试输出

Rust 的测试库默认捕获并丢弃标准输出和标准错误中的输出,除非测试失败了。例如,如果在测试中调用了println!而测试通过了,你将不会在终端看到println!的输出。这个行为可以通过向测试二进制文件传递--nocapture参数来禁用:

$ cargo test -- --nocapture

通过名称来运行测试的子集

有时运行整个测试集会耗费很多时间。如果你负责特定位置的代码,你可能会希望只与这些代码相关的测试。cargo test有一个参数允许你通过指定名称来运行特定的测试。

列表 11-3 中创建了三个如下名称的测试:

Filename: src/lib.rs
#[test]
fn add_two_and_two() {
    assert_eq!(4, 2 + 2);
}

#[test]
fn add_three_and_two() {
    assert_eq!(5, 3 + 2);
}

#[test]
fn one_hundred() {
    assert_eq!(102, 100 + 2);
}

Listing 11-3: Three tests with a variety of names

使用不同的参数会运行不同的测试子集。没有参数的话,如你所见会运行所有的测试:

$ cargo test
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/adder-abcabcabc

running 3 tests
test add_three_and_two ... ok
test one_hundred ... ok
test add_two_and_two ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured

可以传递任意测试的名称来只运行那个测试:

$ cargo test one_hundred
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/adder-abcabcabc

running 1 test
test one_hundred ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

也可以传递名称的一部分,cargo test会运行所有匹配的测试:

$ cargo test add
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/adder-abcabcabc

running 2 tests
test add_three_and_two ... ok
test add_two_and_two ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured

模块名也作为测试名的一部分,所以类似的模块名也可以用来指定测试特定模块。例如,如果将我们的代码组织成一个叫adding的模块和一个叫subtracting的模块并分别带有测试,如列表 11-4 所示:

Filename: src/lib.rs
mod adding {
    #[test]
    fn add_two_and_two() {
        assert_eq!(4, 2 + 2);
    }

    #[test]
    fn add_three_and_two() {
        assert_eq!(5, 3 + 2);
    }

    #[test]
    fn one_hundred() {
        assert_eq!(102, 100 + 2);
    }
}

mod subtracting {
    #[test]
    fn subtract_three_and_two() {
        assert_eq!(1, 3 - 2);
    }
}

Listing 11-4: Tests in two modules named adding and subtracting

执行cargo test会运行所有的测试,而模块名会出现在输出的测试名中:

$ cargo test
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/adder-abcabcabc

running 4 tests
test adding::add_two_and_two ... ok
test adding::add_three_and_two ... ok
test subtracting::subtract_three_and_two ... ok
test adding::one_hundred ... ok

运行cargo test adding将只会运行对应模块的测试而不会运行任何 subtracting 模块中的测试:

$ cargo test adding
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/adder-abcabcabc

running 3 tests
test adding::add_three_and_two ... ok
test adding::one_hundred ... ok
test adding::add_two_and_two ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured

除非指定否则忽略某些测试

有时一些特定的测试执行起来是非常耗费时间的,所以对于大多数cargo test命令,我们希望能排除它。无需为cargo test创建一个用来在运行所有测试时排除特定测试的参数并每次都要记得使用它,我们可以对这些测试使用ignore属性:

Filename: src/lib.rs

#[test]
fn it_works() {
    assert!(true);
}

#[test]
#[ignore]
fn expensive_test() {
    // code that takes an hour to run
}

现在运行测试,将会发现it_works运行了,而expensive_test没有:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished debug [unoptimized + debuginfo] target(s) in 0.24 secs
     Running target/debug/deps/adder-abcabcabc

running 2 tests
test expensive_test ... ignored
test it_works ... ok

test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

我们可以通过cargo test -- --ignored来明确请求只运行那些耗时的测试:

$ cargo test -- --ignored
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/adder-abcabcabc

running 1 test
test expensive_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

通过这种方式,大部分时间运行cargo test将是快速的。当需要检查ignored测试的结果而且你也有时间等待这个结果的话,可以选择执行cargo test -- --ignored