Почему println! не выводит текст в юнит-тестах Rust?
Почему println! молчит в тестах?
В Rust юнит-тесты по умолчанию перехватывают вывод в stdout (и stderr). Это сделано намеренно: тесты должны быть чистыми и не засорять консоль. Когда тест проходит, его stdout подавляется, чтобы не мешать чтению результатов. Если тест падает, вывод показывается автоматически — это помогает в отладке.
Как увидеть вывод?
Есть два основных способа:
1. Флаг --nocapture (самый частый)
Запустите тесты с этим флагом, и весь stdout будет выведен в реальном времени:
cargo test -- --nocapture
Или для одного теста:
cargo test test_debug_output -- --nocapture
Теперь вы увидите:
running 1 test
Значение: 42
test tests::test_debug_output ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
2. Использовать eprintln! для stderr
Вывод в stderr не перехватывается тестовым раннером:
#[cfg(test)]
mod tests {
#[test]
fn test_debug_output() {
let value = 42;
eprintln!("Значение: {}", value); // <- eprintln!
assert_eq!(value, 42);
}
}
Запустите обычный cargo test — вывод появится:
running 1 test
Значение: 42
test tests::test_debug_output ... ok
Но будьте осторожны: eprintln! выводит в stderr, а не в stdout. В большинстве случаев это нормально для отладки, но в CI-системах stderr может считаться ошибкой.
Почему так сделано?
- Чистота вывода: при запуске сотен тестов вывод от каждого сделает консоль нечитаемой.
- Параллелизм: тесты по умолчанию выполняются параллельно. Без перехвата вывод перемешался бы.
- Фокус на результатах: тестовый раннер показывает только итог (ok/failed). Если тест упал — вы увидите и вывод, и панику.
Дополнительные советы
- Используйте
assert!иassert_eq!с понятными сообщениями — это лучше, чемprintln!в тестах. - Для сложной отладки используйте
dbg!(value)— он выводит в stderr и возвращает значение:
#[test]
fn test_with_dbg() {
let value = 42;
let result = dbg!(value * 2); // выведет: [src/lib.rs:10] value * 2 = 84
assert_eq!(result, 84);
}
Запуск с cargo test покажет вывод dbg!, так как он пишет в stderr.
Итог
Используйте cargo test -- --nocapture для временного просмотра stdout или переключитесь на eprintln!/dbg! для постоянного вывода. В "боевых" тестах старайтесь обходиться без отладочного вывода — это делает тесты более читаемыми и надёжными.
Альтернативный подход: логирование через crate log
Если вам нужно больше контроля над выводом в тестах, используйте систему логирования. Это удобно, когда тестов много и вы хотите фильтровать вывод по уровню важности.
Добавьте в Cargo.toml:
[dev-dependencies]
log = "0.4"
env_logger = "0.10"
В тестах:
#[cfg(test)]
mod tests {
use log::info;
#[test]
fn test_with_logging() {
// Инициализация один раз для всех тестов
let _ = env_logger::builder()
.is_test(true) // важно для тестов
.try_init();
let value = 42;
info!("Значение: {}", value);
assert_eq!(value, 42);
}
}
Запускайте с переменной окружения RUST_LOG:
RUST_LOG=info cargo test
Преимущества:
- Вывод структурирован:
[INFO] Значение: 42 - Можно включать/выключать логи по модулям и уровням (error, warn, info, debug, trace)
- Не требует флага
--nocapture— логи выводятся всегда (если уровень позволяет)
Недостаток: нужно добавлять зависимость и инициализировать логгер. Для простых проектов это может быть избыточно.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Локальные и глобальные переменные в Python: как их использовать правильно
Оптимизация производительности C++ приложений: лучшие практики и примеры