6

Почему println! не работает в юнит-тестах Rust?

2

Я реализовал следующий метод и юнит-тест:

use std::fs::File;
use std::path::Path;
use std::io::prelude::*;

fn read_file(path: &Path) {
    let mut file = File::open(path).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    println!("{contents}");
}

#[test]
fn test_read_file() {
    let path = &Path::new("/etc/hosts");
    println!("{path:?}");
    read_file(path);
}

Я запускаю юнит-тест следующим образом:

rustc --test app.rs; ./app

Я также мог бы запустить его с помощью:

cargo test

Однако я получаю сообщение, что тест прошел, но вывод println! никогда не отображается на экране. Почему так происходит?

5 ответ(ов)

7

Это происходит потому, что программы тестов на Rust скрывают стандартный вывод (stdout) успешных тестов, чтобы вывод тестов выглядел более аккуратно. Вы можете отключить это поведение, передав опцию --nocapture тестовому бинарному файлу или команде cargo test (но в этом случае после -- – смотрите ниже):

#[test]
fn test() {
    println!("Скрытый вывод")
}

Запуск тестов:

% rustc --test main.rs; ./main

running 1 тест
test test ... ok

результат теста: ok. 1 пройден; 0 неудач; 0 проигнорировано; 0 измерено

% ./main --nocapture

running 1 тест
Скрытый вывод
test test ... ok

результат теста: ok. 1 пройден; 0 неудач; 0 проигнорировано; 0 измерено

% cargo test -- --nocapture

running 1 тест
Скрытый вывод
test test ... ok

результат теста: ok. 1 пройден; 0 неудач; 0 проигнорировано; 0 измерено

Если тесты не проходят, stdout будет напечатан вне зависимости от наличия этой опции.

1

TL;DR

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

$ cargo test -- --nocapture

Пример кода:

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PieceShape {
    King, Queen, Rook, Bishop, Knight, Pawn
}

fn main() {
    println!("Hello, world!");
}

#[test]
fn demo_debug_format() {
    let q = PieceShape::Queen;
    let p = PieceShape::Pawn;
    let k = PieceShape::King;
    println!("q={:?} p={:?} k={:?}", q, p, k);
}

После запуска команды вы увидите следующий вывод:

Running target/debug/chess-5d475d8baa0176e4

running 1 test
q=Queen p=Pawn k=King
test demo_debug_format ... ok

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

Чтобы включить вывод с помощью println!() и сохранить цвета для результатов тестов, используйте флаги color и nocapture в команде cargo test.

$ cargo test -- --color always --nocapture

(версия cargo: 0.13.0 nightly)

0

При тестировании стандартный вывод не отображается. Вместо текстовых сообщений для тестирования используйте assert!, assert_eq! и fail!. Система юнит-тестов Rust может понимать эти конструкции, но не текстовые сообщения.

Ваш тест будет проходить, даже если что-то пойдет не так. Давайте разберемся, почему:

Сигнатура функции read_to_end выглядит так: fn read_to_end(&mut self) -> IoResult<Vec<u8>>

Она возвращает IoResult, чтобы указать на успех или ошибку. Это просто определение типа для Result, у которого значение ошибки — это IoError. Вам решать, как обрабатывать ошибку. В данном случае мы хотим, чтобы задача завершилась с ошибкой, что достигается вызовом unwrap на Result.

Вот так будет работать:

let contents = File::open(&Path::new("message.txt"))
    .read_to_end()
    .unwrap();

Однако следует помнить, что unwrap не следует использовать слишком часто.

0

Обратите внимание, что современное решение (cargo test -- --show-output) не работает с доктестами, определенными в Markdown-кодовом блоке в строке документации ваших функций. Только операторы println! (и т. д.), выполненные в конкретном блоке #[test], будут учитываться.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь