How it works...
Testing frameworks are a third-party library in many programming languages although well-tested code should be the default! By providing a (tiny) testing framework along with a test runner and even a small benchmarking framework (only on nightly as of this writing), the barrier for testing your Rust code is significantly lower. Although there are still some missing features (for example, mocking), the community is working on providing many of these things via external crates.
After setting everything up in step 1, step 2 creates a singly linked list as the test subject. A singly linked list is a series of the same node types, connected with some sort of pointer. In this recipe, we decided to use the interior mutability pattern, which allows for borrowing mutably at runtime to modify the node it points to. The attached operations (append() and pop()) make use of this pattern. Step 3 then creates the tests that we can use to verify that our code does what we think it should. These tests cover the basic workings of the list: create an empty list, append a few items, and remove them again using pop.
Tests can be failed using a variety of assert! macros. They cover equals (assert_eq!), not equals (assert_ne!), Boolean conditions (assert!), and non-release mode compilation only (debug_assert!). With these available and attributes such as #[should_panic], there is no case that cannot be covered. Additionally, this great Rust book offers an interesting read as well: https://doc.rust-lang.org/book/ch11-01-writing-tests.html.
Step 4 adds a special integration test in a separate file. This restricts programmers to think like the user of the crate, without access to internal modules and functions that can be available in the nested tests module. As a simple test, we insert 10,000 items into the list to see whether it can handle the volume.
Only in step 5 are we ready to run the benchmarks using cargo +nightly test, but tests are not automatically benchmarked. On top of that, benchmarks (cargo +nightly bench) compile the code using --release flags, thereby adding several optimizations that could lead to different outcomes from cargo +nightly test (including a headache for debugging those).
Step 6 shows the output of the benchmarking harness with nanosecond precision for each loop execution (and the standard deviation). Whenever doing any kind of performance optimization, have a benchmark ready to show that it actually worked!
Other nice things that the Rust documentation tool adds to testing are doctests. These are snippets that are compiled and executed as well as rendered as documentation. We were so delighted, we gave it its own recipe! So, let's move on to the next recipe.