576

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ruipacheco
  • 15,025
  • 19
  • 82
  • 138
  • Finding this unintuitive, I've opened [Issue #10777 on GitHub to suggest changing this behavior](https://github.com/rust-lang/cargo/issues/10777#issue-1277499859) – Evan Carroll Aug 01 '23 at 16:24

9 Answers9

648

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.

jiwopene
  • 3,077
  • 17
  • 30
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • 18
    You mentioned passing the `--nocapture` option to `cargo test`, but cargo does not recognize this flag for me (using the latest nightly from rustup.sh). Are you sure it should work? – Jim Garrison Aug 09 '14 at 02:35
  • 63
    @JimGarrison, indeed, [there is an issue](https://github.com/rust-lang/cargo/issues/296) on that. Meanwhile you can use `cargo test -- --nocapture`, it should work. – Vladimir Matveev Aug 09 '14 at 08:13
  • 5
    thanks! unrelated to this question, but that also helped me figure out how to get `cargo test [--] --bench` to work too! – Jim Garrison Aug 09 '14 at 12:37
  • `cargo test -- --no-capture` no longer works. I get the following error: `thread '
    ' panicked at '"Unrecognized option: \'no-capture\'."', ../src/libtest/lib.rs:249`
    – Nashenas Jul 15 '15 at 17:50
  • 11
    @Nashenas, the option is called `nocapture`, not `no-capture`. – Vladimir Matveev Jul 16 '15 at 05:21
  • 1
    Has anyone figured out how to print when debugging in Visual Studio Code in windows? The following task does not print to the popup shell: "cargo test --no-run -- --nocapture" debugger. Note the use of the no-run argument although it does not seem to make a difference either way. All I see is "running 1 test". Awkward tooling. – David Sep 30 '18 at 10:50
  • Its recommended to use `--package` and `--exact` flag if you have a big code base `cargo test --package :::: -- --nocapture --exact`. – Abrar Khan Dec 08 '19 at 16:11
  • 20
    @Anonyme2000 `--nocapture` still works in the 2018 edition. `--show-output` is another option that organizes the output in a format that is easier to see. – L. F. Sep 13 '20 at 01:35
165

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
superlogical
  • 14,332
  • 9
  • 66
  • 76
  • 1
    `cargo test -- --no-capture` no longer works. I get the following error: `thread '
    ' panicked at '"Unrecognized option: \'no-capture\'."', ../src/libtest/lib.rs:249`
    – Nashenas Jul 15 '15 at 17:50
  • I wonder if this issue https://github.com/rust-lang/cargo/issues/1377 is the problem? – superlogical Jul 16 '15 at 20:55
  • 14
    As has been pointed out in previous comments, the option is `--nocapture`, not `--no-capture`. However, that's a completely obvious mistake to make given most command line conventions we tend to come across. I just used this option exactly as described in this answer in rust 1.1 (cargo 0.2.0) and it worked exactly as advertised. – Glenn McAllister Jul 30 '15 at 18:32
59

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.

  • 15
    **Note**: `--show-output` only prints stdout of **successful** tests. – zingi Jul 26 '21 at 06:54
  • 1
    This was helpful. The odd extra -- on the CL confused me. I needed to run just one test & show output. That looks like $ cargo test [test_name] -- --show-output – raddevus Dec 02 '21 at 19:40
  • Yeah, the odd extra `--` is to control that the next arguments are passed to `cargo test`, if I remember correctly. – Raphaël Duchaîne Mar 06 '23 at 22:02
  • @zingi that is not true. According on [documentation](https://doc.rust-lang.org/book/ch11-02-running-tests.html) it shows output of **failed** tests too. – Makich Apr 08 '23 at 09:28
  • 3
    I've just tested it. And indeed it looks like this has changed. `--show-output` prints stdout of successful and failed tests now ( v1.68.2) – zingi Apr 14 '23 at 13:59
  • Currently, failed tests print their output regardless of this flag – Zoey Hewll Aug 15 '23 at 10:18
26

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)

nate
  • 733
  • 8
  • 9
10

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.

A.B.
  • 15,364
  • 3
  • 61
  • 64
  • True information, but not an answer to OP. – BobHy Nov 01 '21 at 04:57
  • I think this is Rust's way to prevent us from shooting yourself in the foot. Second form of assert is exactly what logging should do, but another way around. Instead of painstakingly write 10K+ logs when all OK, write when something is NOT. `std::module_path` macro may be useful too. – dobhareach Jul 08 '22 at 14:39
5

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.

Colin Woodbury
  • 1,799
  • 2
  • 15
  • 25
3

It's likely that the test output is being captured by the testing framework and not being printed to the standard output. When running tests with cargo test, the output of each test is captured and displayed only if the test fails. If you want to see the output of a test, you can use the --nocapture flag when running the test with cargo test. Like so:

cargo test -- --nocapture

Or you can use the println! macro inside a test function to print output to the standard output. Like so:

 #[test]
 fn test_read_file() {
    let path = &Path::new("/etc/hosts");
    println!("{:?}", path);
    read_file(path);
    println!("The test passed!");
}
AlexaP
  • 179
  • 2
  • 1
1

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

blacha
  • 93
  • 2
  • 7
1

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

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

sudo might be optional depending on your set-up.

Caridorc
  • 6,222
  • 2
  • 31
  • 46