Почему println! не работает в юнит-тестах Rust?
Я реализовал следующий метод и юнит-тест:
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 ответ(ов)
Это происходит потому, что программы тестов на 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 будет напечатан вне зависимости от наличия этой опции.
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
Чтобы включить вывод с помощью println!()
и сохранить цвета для результатов тестов, используйте флаги color
и nocapture
в команде cargo test
.
$ cargo test -- --color always --nocapture
(версия cargo: 0.13.0 nightly)
При тестировании стандартный вывод не отображается. Вместо текстовых сообщений для тестирования используйте 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
не следует использовать слишком часто.
Обратите внимание, что современное решение (cargo test -- --show-output
) не работает с доктестами, определенными в Markdown-кодовом блоке в строке документации ваших функций. Только операторы println!
(и т. д.), выполненные в конкретном блоке #[test]
, будут учитываться.