Avatar ·

Почему println! не выводит текст в юнит-тестах Rust?

Всем привет! Я только начал изучать Rust и столкнулся со странной проблемой. Пишу простой юнит-тест для функции, которая складывает два числа. Хочу вывести результат в консоль для отладки, но `println!` внутри теста ничего не выводит. Вот мой код: ```rust fn add(a: i32, b: i32) -> i32 { a + b } #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { let result = add(2, 3); println!("Результат: {}", result); // Ничего не выводится! assert_eq!(result, 5); } } ``` Я запускаю тест командой `cargo test` и вижу только сообщение "running 1 test" и "test result: ok". Никакого вывода в консоль нет. Пробовал даже использовать `println!("Hello")` в самом начале теста — тоже тишина. Я ожидал, что `println!` будет работать как в обычной программе. Может быть, это баг компилятора? Или я неправильно использую тесты? Подскажите, пожалуйста, как вывести отладочную информацию в тестах Rust. Заранее спасибо! <<>>

Причина: тесты по умолчанию подавляют вывод

Вы столкнулись с ожидаемым поведением Rust — это не баг, а особенность системы тестирования. Когда вы запускаете тесты командой cargo test, все вызовы println! внутри успешных тестов автоматически подавляются. Вывод появляется только для провалившихся тестов. Это сделано для того, чтобы консоль не засорялась отладочной информацией при прогоне сотен тестов.

Решение 1: Запускайте тесты с флагом --nocapture

Самый простой способ увидеть вывод println! — передать флаг --nocapture (или --show-output):

cargo test -- --nocapture

Теперь ваш тест выведет:

running 1 test
Результат: 5
test tests::test_add ... ok

Флаг -- отделяет аргументы для cargo test от аргументов для самого тестового раннера.

Решение 2: Используйте макрос eprintln! для stderr

Если вы хотите видеть вывод только при ошибках, используйте eprintln! — он пишет в stderr и не подавляется:

#[test]
fn test_add() {
    let result = add(2, 3);
    eprintln!("Отладка: результат = {}", result);
    assert_eq!(result, 5);
}

При запуске cargo test вывод будет:

running 1 test
Отладка: результат = 5
test tests::test_add ... ok

Решение 3: Создайте тест, который заведомо провалится

Если нужно быстро проверить вывод без изменения флагов, добавьте временный assert!(false):

#[test]
fn debug_test() {
    println!("Значение: {}", 42);
    assert!(false); // Теперь вывод появится
}

Почему это так работает?

  • Производительность: Вывод в консоль — медленная операция. При 10 000 тестах подавление вывода ускоряет прогон.
  • Чистота: Вы видите только результаты тестов (ok/fail), а не мешанину из отладочных сообщений.
  • CI/CD: В системах непрерывной интеграции (GitHub Actions, Jenkins) подавленный вывод не засоряет логи.

Важно: не используйте println! для ассертов

Помните, что println! не влияет на результат теста. Если вам нужно проверить значение, используйте assert_eq! или assert!. Например:

#[test]
fn test_add() {
    let result = add(2, 3);
    println!("Отладка: {}", result); // Вывод только с --nocapture
    assert_eq!(result, 5);          // Реальная проверка
}
<<>>

Альтернативный подход: логирование через библиотеку log

Если вам нужно более гибкое управление выводом, используйте крейт log с env_logger. Это позволит включать/отключать отладку без изменения кода тестов:

// Cargo.toml
[dependencies]
log = "0.4"
env_logger = "0.10"

// В тесте:
#[test]
fn test_with_log() {
    env_logger::init(); // Инициализация один раз
    log::info!("Результат: {}", add(2, 3));
    assert_eq!(add(2, 3), 5);
}

Запускайте с переменной окружения:

RUST_LOG=info cargo test -- --nocapture

Для VSCode и IntelliJ

В редакторах кода можно добавить --nocapture в конфигурацию запуска тестов. В VSCode откройте .vscode/settings.json и добавьте:

"rust-analyzer.runnables.extraArgs": ["--nocapture"]

Когда использовать каждый метод?

  • --nocapture: Быстрая отладка одного теста.
  • eprintln!: Постоянная отладка, видимая всегда.
  • log: Профессиональные проекты с уровнями логирования.