控制测试的运行方式

就像编译代码然后运行生成的二进制文件一样,在测试模式下编译代码并运行生成的测试 二元的。生成的二进制文件的默认行为是运行 并行所有测试并捕获测试运行期间生成的输出, 防止显示输出并使其更易于读取 与测试结果相关的输出。但是,您可以指定命令行 选项来更改此默认行为。cargo runcargo testcargo test

一些命令行选项转到 ,一些选项转到生成的测试 二元的。要分隔这两种类型的参数,请列出 go to 后跟分隔符,然后是 go to 测试二进制文件。Running (正在运行) 将显示您可以使用的选项 和 running 将显示选项 可以在分隔符之后使用。cargo testcargo test--cargo test --helpcargo testcargo test -- --help

并行或连续运行测试

当您运行多个测试时,默认情况下,它们使用线程 这意味着他们更快地完成运行,您也能更快地获得反馈。因为 测试同时运行,您必须确保您的测试不依赖于 彼此共享或任何共享状态(包括共享环境),例如 当前工作目录或环境变量。

例如,假设您的每个测试都运行一些代码,该代码在磁盘上创建一个文件 命名 test-output.txt 并将一些数据写入该文件。然后每个测试都会读取 该文件中的数据,并断言该文件包含特定值 这在每次测试中都是不同的。由于测试同时运行,因此一个 test 可能会在另一个测试写入和 读取文件。第二个测试将失败,而不是因为代码是 不正确,但因为测试在运行时相互干扰 并行。一种解决方案是确保每个测试写入不同的文件; 另一种解决方案是一次运行一个测试。

如果您不想并行运行测试,或者希望更精细地运行测试 控制使用的线程数,可以发送标志 以及要用于测试二进制文件的线程数。查看 以下示例:--test-threads

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

我们将测试线程数设置为 ,告诉程序不要使用任何 排比。使用一个线程运行测试比运行测试花费的时间更长 它们并行,但如果它们共享,则测试不会相互干扰 州。1

显示函数输出

默认情况下,如果测试通过,Rust 的测试库会捕获打印到 标准输出。例如,如果我们调用一个测试,并且 传递,我们不会在终端中看到输出;我们只会看到 行,指示测试通过。如果测试失败,我们将看到 打印到标准输出以及失败消息的其余部分。println!println!

举个例子,示例 11-10 有一个 silly 函数,它打印了 参数并返回 10,以及一个通过的测试和一个失败的测试。

文件名: src/lib.rs
fn prints_and_returns_10(a: i32) -> i32 {
    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!(value, 10);
    }

    #[test]
    fn this_test_will_fail() {
        let value = prints_and_returns_10(8);
        assert_eq!(value, 5);
    }
}
示例 11-10:对调用println!

当我们使用 运行这些测试时,我们将看到以下输出:cargo test

$ cargo test
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)

running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'tests::this_test_will_fail' panicked at src/lib.rs:19:9:
assertion `left == right` failed
  left: 10
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::this_test_will_fail

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

请注意,在此输出中,我们没有看到 ,即 在通过的测试运行时打印。该输出已被捕获。这 失败的测试的输出 将显示在 部分 ,其中还显示了测试失败的原因。I got the value 4I got the value 8

如果我们也想看到通过测试的打印值,我们可以告诉 Rust 还显示成功测试的输出 :--show-output

$ cargo test -- --show-output

当我们再次使用标志运行示例 11-10 中的测试时,我们 请参阅以下输出:--show-output

$ cargo test -- --show-output
   Compiling silly-function v0.1.0 (file:///projects/silly-function)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)

running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok

successes:

---- tests::this_test_will_pass stdout ----
I got the value 4


successes:
    tests::this_test_will_pass

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'tests::this_test_will_fail' panicked at src/lib.rs:19:9:
assertion `left == right` failed
  left: 10
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::this_test_will_fail

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

按名称运行测试子集

有时,运行完整的测试套件可能需要很长时间。如果您正在处理 代码中,您可能希望仅运行与 那个代码。您可以通过传递名称 或要作为参数运行的测试的名称。cargo test

为了演示如何运行测试子集,我们首先为 我们的函数,如示例 11-11 所示,然后选择要运行的函数。add_two

文件名: src/lib.rs
pub fn add_two(a: usize) -> usize {
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_two_and_two() {
        let result = add_two(2);
        assert_eq!(result, 4);
    }

    #[test]
    fn add_three_and_two() {
        let result = add_two(3);
        assert_eq!(result, 5);
    }

    #[test]
    fn one_hundred() {
        let result = add_two(100);
        assert_eq!(result, 102);
    }
}
示例 11-11:具有三个不同名称的三个测试

如果我们在不传递任何参数的情况下运行测试,正如我们之前看到的,所有的 测试将并行运行:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

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

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

运行单个测试

我们可以将任何测试函数的名称传递给 以仅运行该测试:cargo test

$ cargo test one_hundred
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.69s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::one_hundred ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in 0.00s

仅运行名称为 的测试;其他两个测试不匹配 那个名字。测试输出让我们知道我们还有更多测试没有由 显示在最后。one_hundred2 filtered out

我们不能以这种方式指定多个测试的名称;仅第一个值 given to 将被使用。但是有一种方法可以运行多个测试。cargo test

筛选以运行多个测试

我们可以指定测试名称的一部分,以及名称与该值匹配的任何测试 将运行。例如,因为我们的两个测试名称包含 ,所以我们可以 通过运行 来运行这两个 :addcargo test add

$ cargo test add
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

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

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s

此命令运行名称中包含的所有测试并筛选出测试 叫。另请注意,显示测试的模块将变为 部分,因此我们可以通过筛选 在模块的名称上。addone_hundred

除非特别要求,否则忽略某些测试

有时,执行一些特定的测试可能非常耗时,因此您需要 可能希望在大多数 运行期间排除它们。而不是 将你想要运行的所有测试列为参数,你可以改为注释 使用 attribute 来排除它们进行耗时的测试,如图所示 这里:cargo testignore

文件名: src/lib.rs

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }

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

之后,我们将行添加到要排除的测试中。 现在,当我们运行测试时,会运行,但不运行:#[test]#[ignore]it_worksexpensive_test

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

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

test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

该函数列为 .如果我们想只运行 忽略的测试,我们可以使用:expensive_testignoredcargo test -- --ignored

$ cargo test -- --ignored
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test expensive_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

通过控制运行哪些测试,您可以确保结果 将很快返回。当您处于需要检查 测试结果,您有时间等待结果, 您可以改为运行。如果要运行所有测试 无论它们是否被忽略,您都可以运行 .cargo testignoredcargo test -- --ignoredcargo test -- --include-ignored

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准