3 > Program testing can be a very effective way to show the presence of bugs, but
4 > it is hopelessly inadequate for showing their absence.
6 > Edsger W. Dijkstra, "The Humble Programmer" (1972)
8 Let's talk about how to test Rust code. What we will not be talking about is
9 the right way to test Rust code. There are many schools of thought regarding
10 the right and wrong way to write tests. All of these approaches use the same
11 basic tools, and so we'll show you the syntax for using them.
13 # The `test` attribute
15 At its simplest, a test in Rust is a function that's annotated with the `test`
16 attribute. Let's make a new project with Cargo called `adder`:
23 Cargo will automatically generate a simple test when you make a new project.
24 Here's the contents of `src/lib.rs`:
33 Note the `#[test]`. This attribute indicates that this is a test function. It
34 currently has no body. That's good enough to pass! We can run the tests with
39 Compiling adder v0.0.1 (file:///home/you/projects/adder)
40 Running target/adder-91b3e234d4ed382a
45 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
51 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
54 Cargo compiled and ran our tests. There are two sets of output here: one
55 for the test we wrote, and another for documentation tests. We'll talk about
56 those later. For now, see this line:
62 Note the `it_works`. This comes from the name of our function:
69 We also get a summary line:
72 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
75 So why does our do-nothing test pass? Any test which doesn't `panic!` passes,
76 and any test that does `panic!` fails. Let's make our test fail:
86 `assert!` is a macro provided by Rust which takes one argument: if the argument
87 is `true`, nothing happens. If the argument is `false`, it `panic!`s. Let's run
92 Compiling adder v0.0.1 (file:///home/you/projects/adder)
93 Running target/adder-91b3e234d4ed382a
96 test it_works ... FAILED
100 ---- it_works stdout ----
101 thread 'it_works' panicked at 'assertion failed: false', /home/steve/tmp/adder/src/lib.rs:3
108 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
110 thread '<main>' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247
113 Rust indicates that our test failed:
116 test it_works ... FAILED
119 And that's reflected in the summary line:
122 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
125 We also get a non-zero status code. We can use `$?` on OS X and Linux:
132 On Windows, if you’re using `cmd`:
138 And if you’re using PowerShell:
141 > echo $LASTEXITCODE # the code itself
142 > echo $? # a boolean, fail or succeed
145 This is useful if you want to integrate `cargo test` into other tooling.
147 We can invert our test's failure with another attribute: `should_panic`:
158 This test will now succeed if we `panic!` and fail if we complete. Let's try it:
162 Compiling adder v0.0.1 (file:///home/you/projects/adder)
163 Running target/adder-91b3e234d4ed382a
168 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
174 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
177 Rust provides another macro, `assert_eq!`, that compares two arguments for
185 assert_eq!("Hello", "world");
189 Does this test pass or fail? Because of the `should_panic` attribute, it
194 Compiling adder v0.0.1 (file:///home/you/projects/adder)
195 Running target/adder-91b3e234d4ed382a
200 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
206 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
209 `should_panic` tests can be fragile, as it's hard to guarantee that the test
210 didn't fail for an unexpected reason. To help with this, an optional `expected`
211 parameter can be added to the `should_panic` attribute. The test harness will
212 make sure that the failure message contains the provided text. A safer version
213 of the example above would be:
218 #[should_panic(expected = "assertion failed")]
220 assert_eq!("Hello", "world");
224 That's all there is to the basics! Let's write one 'real' test:
228 pub fn add_two(a: i32) -> i32 {
234 assert_eq!(4, add_two(2));
238 This is a very common use of `assert_eq!`: call some function with
239 some known arguments and compare it to the expected output.
241 # The `ignore` attribute
243 Sometimes a few specific tests can be very time-consuming to execute. These
244 can be disabled by default by using the `ignore` attribute:
250 assert_eq!(4, add_two(2));
255 fn expensive_test() {
256 // code that takes an hour to run
260 Now we run our tests and see that `it_works` is run, but `expensive_test` is
265 Compiling adder v0.0.1 (file:///home/you/projects/adder)
266 Running target/adder-91b3e234d4ed382a
269 test expensive_test ... ignored
272 test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured
278 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
281 The expensive tests can be run explicitly using `cargo test -- --ignored`:
284 $ cargo test -- --ignored
285 Running target/adder-91b3e234d4ed382a
288 test expensive_test ... ok
290 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
296 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
299 The `--ignored` argument is an argument to the test binary, and not to Cargo,
300 which is why the command is `cargo test -- --ignored`.
304 There is one way in which our existing example is not idiomatic: it's
305 missing the `tests` module. The idiomatic way of writing our example
310 pub fn add_two(a: i32) -> i32 {
320 assert_eq!(4, add_two(2));
325 There's a few changes here. The first is the introduction of a `mod tests` with
326 a `cfg` attribute. The module allows us to group all of our tests together, and
327 to also define helper functions if needed, that don't become a part of the rest
328 of our crate. The `cfg` attribute only compiles our test code if we're
329 currently trying to run the tests. This can save compile time, and also ensures
330 that our tests are entirely left out of a normal build.
332 The second change is the `use` declaration. Because we're in an inner module,
333 we need to bring our test function into scope. This can be annoying if you have
334 a large module, and so this is a common use of globs. Let's change our
335 `src/lib.rs` to make use of it:
339 pub fn add_two(a: i32) -> i32 {
349 assert_eq!(4, add_two(2));
354 Note the different `use` line. Now we run our tests:
358 Updating registry `https://github.com/rust-lang/crates.io-index`
359 Compiling adder v0.0.1 (file:///home/you/projects/adder)
360 Running target/adder-91b3e234d4ed382a
363 test tests::it_works ... ok
365 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
371 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
376 The current convention is to use the `tests` module to hold your "unit-style"
377 tests. Anything that tests one small bit of functionality makes sense to
378 go here. But what about "integration-style" tests instead? For that, we have
379 the `tests` directory.
381 # The `tests` directory
383 To write an integration test, let's make a `tests` directory, and
384 put a `tests/lib.rs` file inside, with this as its contents:
392 assert_eq!(4, adder::add_two(2));
396 This looks similar to our previous tests, but slightly different. We now have
397 an `extern crate adder` at the top. This is because the tests in the `tests`
398 directory are an entirely separate crate, and so we need to import our library.
399 This is also why `tests` is a suitable place to write integration-style tests:
400 they use the library like any other consumer of it would.
406 Compiling adder v0.0.1 (file:///home/you/projects/adder)
407 Running target/adder-91b3e234d4ed382a
410 test tests::it_works ... ok
412 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
414 Running target/lib-c18e7d3494509e74
419 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
425 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
428 Now we have three sections: our previous test is also run, as well as our new
431 That's all there is to the `tests` directory. The `tests` module isn't needed
432 here, since the whole thing is focused on tests.
434 Let's finally check out that third section: documentation tests.
436 # Documentation tests
438 Nothing is better than documentation with examples. Nothing is worse than
439 examples that don't actually work, because the code has changed since the
440 documentation has been written. To this end, Rust supports automatically
441 running examples in your documentation (**note:** this only works in library
442 crates, not binary crates). Here's a fleshed-out `src/lib.rs` with examples:
446 //! The `adder` crate provides functions that add numbers to other numbers.
451 //! assert_eq!(4, adder::add_two(2));
454 /// This function adds two to its argument.
459 /// use adder::add_two;
461 /// assert_eq!(4, add_two(2));
463 pub fn add_two(a: i32) -> i32 {
473 assert_eq!(4, add_two(2));
478 Note the module-level documentation with `//!` and the function-level
479 documentation with `///`. Rust's documentation supports Markdown in comments,
480 and so triple graves mark code blocks. It is conventional to include the
481 `# Examples` section, exactly like that, with examples following.
483 Let's run the tests again:
487 Compiling adder v0.0.1 (file:///home/steve/tmp/adder)
488 Running target/adder-91b3e234d4ed382a
491 test tests::it_works ... ok
493 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
495 Running target/lib-c18e7d3494509e74
500 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
505 test add_two_0 ... ok
508 test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
511 Now we have all three kinds of tests running! Note the names of the
512 documentation tests: the `_0` is generated for the module test, and `add_two_0`
513 for the function test. These will auto increment with names like `add_two_1` as
514 you add more examples.
516 We haven’t covered all of the details with writing documentation tests. For more,
517 please see the [Documentation chapter](documentation.html).