testing-rust

Examples of how to use a range of testing features with the Rust programming language

Stars
24

Testing Rust Code

This repo contains examples of many common features and approaches for testing Rust code. Some of the features come out of the box, and others from libraries. I tried to pick the most popular tools for each purpose, since they work well for most common use cases and you're likely to encounter these libraries in real world projects.

Our example project is just one trivially simple function (factorial) that's built both as a library and a command line application. That way we can demonstrate the testing of both libraries and executables.

Links to Examples

  • Basic unit tests are the simplest thing to get started with. Rust supports these out of the box, without any libraries.
  • Doc tests are a really cool Rust feature that executes code that's included in the doc comments. This can be used to check for correctness for very simple functions, but oftentimes you'll want to have regular unit tests for more complex functions. They also ensure that the examples included in your comments are actually correct! Doc tests can be attached to just about anything, not just functions. More info on doc tests can be found here
  • Property based testing is an excellent approach for testing certian types of functions. This approach to testing was popularized by a Haskell library called QuickCheck, but now just about every language has some similar library available. For this example, we're using the Rust quickcheck crate. The nice thing about property-based testing is that it can quickly find boundary conditions.
  • Library integration tests allow you to write integration tests that use your library just like a normal dependency. This forces you to use only the public API that's exposed by your library. Integration tests are supported out of the box, without any additional libraries.
  • Application integration tests allow you to test the public api of an executable. Rust is particularly great for creating command line interfaces, so this example shows off the excellent assert_cmd crate.
  • Benchmarks are often overlooked form of testing, but they can be extremely important in some cases. Rust does have some built-in support for benchmarks, but the Criterion crate is really the standard for benchmarking in Rust, and it's not hard to see why. Criterion has just about every feature you could need, including the ability to compare benchmarks across different builds, and even generate reports with fancy graphs.
  • Fuzz testing is a really great approach for finding panics and memory-safety issues. Here we use cargo-fuzz to help setup and execute the fuzz tests. The actual fuzz tests are run by libFuzzer, though cargo-fuzz also supports AFL. Fuzz testing requires a bit more in the way of setup than the other techniques, but cargo-fuzz makes it all pretty painless. To start with, you'll need to have the nightly rust toolchain installed. Run rustup update nightly if you don't have that already. To run the example fuzz tests, execute cargo +nightly fuzz run fuzz_factorial_interface -- -max_total_time=60. This will run the fuzzer for 60 seconds. If you leave off the max_total_time, then the fuzzer will just run until you stop it with ctrl+c. Take a look at the libFuzzer docs on how to interpret the output. If you hit a panic or a segfault, it'll be obvious.