Testing Rust code

Unit testing

Unit tests are shipped together with the implementation code of a crate. They have access to all code entities in the implementing module, no matter their visibility from other modules.

Exercise 2.a: add a sub-module tests to the library crate of your accountmgr package. Add to it basic unit tests to convince yourself that the functionalities you have implemented thus far are correct. It is up to you what test cases you want to use, but follow the usual rule of thumbs of testing weird/edge cases as well as some easy ones. Make sure that cargo test pass. Make sure that you also test the internal private state of your values (e.g., that a password field is properly set to the same value passed to the constructor), because unit tests are your only chance to do so.

Integration testing

Integration tests are shipped in separate files located under the top-level tests/ directory and are compiled to independent binary crates linked to the main library crate. As such, they can only access the public API of your library crate and not its private entities.

Exercise 2.b: move all the tests you can from the tests sub-module to integration tests located under tests/ and make sure that cargo test still pass. It should be possible to turn most of them to integration tests, but not all of them. Which tests couldn't you turn into integration tests and why?

Documentation tests

Documentation tests are integration tests that reside alongside with the implementation code, in docstrings.

Exercise 2.c: Add documentation to your code in the form of docstrings. At the very minimum, for each public entity in the module provide a succinct explanation of what it does. Check the documentation of rustdoc on how to write documentation and the details about the docstring markup language. Verify by running cargo doc that documentation is properly generated under target/doc/ for all the crates related to your package.

Exercise 2.d: Either add new or turn some of your existing integration tests into documentation tests integrated into your docstrings. Try to come up with doctests that are actually useful as documentation, because that is one of the key points of doctests. For example, have you documented what should happen when trying to parse: a (1) well-formed account string, (2) an empty account string, (3) a non-empty account string that lacks the colon separator? If not, this is your chance to do so! Make sure that cargo test still pass.

Code coverage

In a previous lab session you have seen how to use code coverage in C/C++ to guide the addition of test cases, so that all your code is exercised during the execution of a test suite. Code coverage tooling is available for Rust as well. You are going to give a try to Tarpaulin a code coverage tool for Rust, that can trace exercised code during test suite execution via either ptrace on Linux/x86_64 (by default) or LLVM's SanitizerCoverage (passing the --engine llvm flag, supported on platforms other than Linux/x86_64).

Exercise 2.e: Read the documentation of tarpaulin crate and install it so that the cargo tarpaulin command become available in your Rust installation.

When you run cargo tarpaulin in a Rust package, it will run the same tests of cargo test (in fact: Tarpaulin even strives to offer a fully-compatible CLI), but under code coverage instrumentation. At the end of the execution Tarpaulin will emit a report about covered/non-covered lines for each module. A coverage "diff" w.r.t. the previous run will also be emitted. Note that currently Tarpaulin only offers line coverage; branch coverage is not implemented yet.

Exercise 2.f: Run cargo tarpaulin on your package and verify what's your current line coverage. By default, only a succinct textual report is returned; try adding the --out html flag to obtain a browsable version of your code, with covered/non-covered lines highlighted.

If your line coverage is not 100%, try to reach that percentage by adding new test cases that cover the missing lines. If it's already 100% go back to Exercise 1.c, add a missing functionality, verify it decreases line coverage, and then make it reach full coverage again.