ruipacheco
ruipacheco

Reputation: 16402

Why doesn't println! work in Rust unit tests?

I've implemented the following method and unit test:

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);
}

I run the unit test this way:

rustc --test app.rs; ./app

I could also run this with

cargo test

I get a message back saying the test passed but the println! is never displayed on screen. Why not?

Upvotes: 657

Views: 221091

Answers (8)

Caridorc
Caridorc

Reputation: 6641

In case you want to run the test displaying the printed output everytime the file changes:

cargo watch -x "test -- --nocapture"

Upvotes: 5

Preface: This answer reflects a change in Rust 1.41 ("You can now pass --show-output argument to test binaries to print the output of successful tests." per the CHANGELOG)


As mentioned by L. F., --show-output is the way to go.

$ cargo test -- --show-output

Other display flags are mentioned in the documentation of cargo test in display-options.

Upvotes: 85

Colin Woodbury
Colin Woodbury

Reputation: 1809

Note that the modern solution (cargo test -- --show-output) doesn't work in doctests defined in a Markdown code-fence in the docstring of your functions. Only println! (etc.) statements done in a concrete #[test] block will be respected.

Upvotes: 5

blacha
blacha

Reputation: 141

Why? I don't know, but there is a small hack eprintln!("will print in {}", "tests")

Upvotes: 1

Vladimir Matveev
Vladimir Matveev

Reputation: 127711

This happens because Rust test programs hide the stdout of successful tests in order for the test output to be tidy. You can disable this behavior by passing the --nocapture option to the test binary or to cargo test (but, in this case after -- – see below):

#[test]
fn test() {
    println!("Hidden output")
}

Invoking tests:

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

running 1 test
test test ... ok

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

% ./main --nocapture

running 1 test
Hidden output
test test ... ok

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

% cargo test -- --nocapture

running 1 test
Hidden output
test test ... ok

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

If tests fail, however, their stdout will be printed regardless if this option is present or not.

Upvotes: 752

nate
nate

Reputation: 793

To include print outs with println!() and keep colors for the test results, use the color and nocapture flags in cargo test.

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

(cargo version: 0.13.0 nightly)

Upvotes: 29

superlogical
superlogical

Reputation: 14950

TL;DR

$ cargo test -- --nocapture

With the following code:

#[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);
}

Then run the following:

 $ cargo test -- --nocapture

And you should see

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

Upvotes: 192

A.B.
A.B.

Reputation: 16630

While testing, standard output is not displayed. Don't use text messages for testing but assert!, assert_eq!, and fail! instead. Rust's unit test system can understand these but not text messages.

The test you have written will pass even if something goes wrong. Let's see why:

read_to_end's signature is fn read_to_end(&mut self) -> IoResult<Vec<u8>>

It returns an IoResult to indicate success or error. This is just a type def for a Result whose error value is an IoError. It's up to you to decide how an error should be handled. In this case, we want the task to fail, which is done by calling unwrap on the Result.

This will work:

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

unwrap should not be overused though.

Upvotes: 13

Related Questions