Управление запуском тестов

RustRustBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Добро пожаловать в Управление выполнением тестов. Этот лаба является частью Rust Book. Вы можете практиковать свои навыки Rust в LabEx.

В этом лабе вы научитесь управлять поведением выполнения тестов в Rust с использованием командной строки для cargo test и результирующего бинарника теста.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") subgraph Lab Skills rust/variable_declarations -.-> lab-100416{{"Управление запуском тестов"}} rust/integer_types -.-> lab-100416{{"Управление запуском тестов"}} rust/function_syntax -.-> lab-100416{{"Управление запуском тестов"}} rust/expressions_statements -.-> lab-100416{{"Управление запуском тестов"}} end

Управление выполнением тестов

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

有些命令行选项会传递给 cargo test,有些则会传递给生成的测试二进制文件。为了区分这两种类型的参数,你先列出传递给 cargo test 的参数,后面跟着分隔符 --,然后再列出传递给测试二进制文件的参数。运行 cargo test --help 会显示你可以与 cargo test 一起使用的选项,运行 cargo test -- --help 会显示在分隔符之后可以使用的选项。

Запуск тестов параллельно или последовательно

Когда вы запускаете несколько тестов, по умолчанию они запускаются параллельно с использованием потоков, что означает, что они завершают выполнение быстрее и вы получаете обратную связь быстрее. Поскольку тесты запускаются одновременно, вы должны убедиться, что ваши тесты не зависят друг от друга или от какого-либо общего состояния, включая общий окружающий контекст, такой как текущая рабочая директория или переменные окружения.

Например, предположим, что каждый из ваших тестов выполняет некоторый код, который создает файл на диске с именем test-output.txt и записывает в него некоторые данные. Затем каждый тест читает данные из этого файла и утверждает, что файл содержит определенное значение, которое различается в каждом тесте. Поскольку тесты запускаются одновременно, один тест может перезаписать файл в промежутке между записью и чтением файла другим тестом. Второй тест тогда будет завершаться с ошибкой, не потому, что код неверен, а потому, что тесты вмешиваются друг с другом при параллельном выполнении. Одним из решений является обеспечение того, чтобы каждый тест записывал в отдельный файл; другое решение — запускать тесты по одному.

Если вы не хотите запускать тесты параллельно или если вы хотите более детального контроля над количеством используемых потоков, вы можете передать флаг --test-threads и количество потоков, которое вы хотите использовать, в бинарник теста. Посмотрите на следующий пример:

cargo test -- --test-threads=1

Мы установили количество тестовых потоков равным 1, тем самым сообщаем программе не использовать никакой параллелизм. Запуск тестов с использованием одного потока займет больше времени, чем параллельный запуск, но тесты не будут мешать друг другу, если они используют общие ресурсы.

Показ вывода функции

По умолчанию, если тест проходит, тестовая библиотека Rust захватывает все, что выводится в стандартный вывод. Например, если мы вызываем println! в тесте и тест проходит, мы не увидим вывод println! в терминале; мы увидим только строку, которая показывает, что тест прошел. Если тест завершается с ошибкой, мы увидим все, что было выведено в стандартный вывод, вместе с остальной информацией об ошибке.

В качестве примера, в Листинге 11-10 есть простая функция, которая выводит значение своего параметра и возвращает 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!(10, value);
    }

    #[test]
    fn this_test_will_fail() {
        let value = prints_and_returns_10(8);
        assert_eq!(5, value);
    }
}

Листинг 11-10: Тесты для функции, которая вызывает println!

Когда мы запускаем эти тесты с помощью cargo test, мы увидим следующий вывод:

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

failures:

---- tests::this_test_will_fail stdout ----
1 I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `10`', src/lib.rs:19:9
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

Заметим, что нигде в этом выводе мы не видим I got the value 4, которое выводится, когда проходит тест. Этот вывод был захвачен. Вывод из теста, который завершается с ошибкой, I got the value 8 [1], появляется в разделе вывода сводки по тестам, который также показывает причину неудачи теста.

Если мы хотим также увидеть выведенные значения для проходящих тестов, мы можем попросить Rust также показать вывод успешных тестов с помощью флага --show-output:

cargo test -- --show-output

Когда мы снова запускаем тесты из Листинга 11-10 с флагом --show-output, мы видим следующий вывод:

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

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 'main' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `10`', src/lib.rs:19:9
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

Запуск подмножества тестов по имени

Иногда запуск полного набора тестов может занять много времени. Если вы работаете над кодом в определенной области, вы, возможно, захотите запустить только тесты, относящиеся к этому коду. Вы можете выбрать, какие тесты запускать, передав в качестве аргумента cargo test имя или имена тестов, которые вы хотите запустить.

Для демонстрации того, как запускать подмножество тестов, мы сначала создадим три теста для нашей функции add_two, как показано в Листинге 11-11, и выберем, какие из них запускать.

Имя файла: src/lib.rs

pub fn add_two(a: i32) -> i32 {
    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));
    }
}

Листинг 11-11: Три теста с тремя разными именами

Если мы запускаем тесты без передачи любых аргументов, как мы видели ранее, все тесты будут запускаться параллельно:

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

Запуск отдельных тестов

Мы можем передать имя любой тестовой функции в cargo test, чтобы запустить только этот тест:

Запустился только тест с именем one_hundred; другие два теста не соответствовали этому имени. Вывод теста сообщает нам, что у нас были дополнительные тесты, которые не запустились, показывая 2 filtered out в конце.

Мы не можем указать имена нескольких тестов таким образом; будет использовано только первое значение, переданное в cargo test. Но есть способ запустить несколько тестов.

Фильтрация для запуска нескольких тестов

Мы можем указать часть имени теста, и все тесты, чье имя соответствует этому значению, будут запущены. Например, поскольку в именах двух наших тестов содержится add, мы можем запустить эти два теста, выполнив cargo test add:

Эта команда запустила все тесты, в именах которых содержится add, и проигнорировала тест с именем one_hundred. Также обратите внимание, что модуль, в котором появляется тест, становится частью имени теста, поэтому мы можем запустить все тесты в модуле, выполнив фильтрацию по имени модуля.

Игнорирование некоторых тестов, если не требуется их запуск явно

Иногда некоторые конкретные тесты могут быть очень затратными по времени на выполнение, поэтому вы, возможно, захотите исключить их при большинстве запусков cargo test. Вместо того, чтобы перечислять в качестве аргументов все тесты, которые вы хотите запустить, вы можете вместо этого пометить затратные тесты с помощью атрибута ignore, чтобы исключить их, как показано ниже:

Имя файла: src/lib.rs

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

#[test]
#[ignore]
fn expensive_test() {
    // код, который занимает час на выполнение
}

После #[test] мы добавляем строку #[ignore] к тесту, который мы хотим исключить. Теперь, когда мы запускаем наши тесты, it_works запускается, а expensive_test нет:

Функция expensive_test перечислена как ignored. Если мы хотим запустить только игнорируемые тесты, мы можем использовать cargo test -- --ignored:

Управляя тем, какие тесты запускаются, вы можете убедиться, что результаты cargo test будут возвращены быстро. Когда вы достигнете такой точки, где имеет смысл проверить результаты ignored тестов и у вас есть время ожидать результатов, вы можете вместо этого запустить cargo test -- --ignored. Если вы хотите запустить все тесты, независимо от того, игнорируются они или нет, вы можете запустить cargo test -- --include-ignored.

Резюме

Поздравляем! Вы завершили лабораторную работу по управлению запуском тестов. Вы можете практиковаться в более лабораторных работах в LabEx, чтобы улучшить свои навыки.