Rust testing libraries You should know about
Cargo provides a great built-in testing framework, it's powerful and sometimes sufficient, but it can be extended with other testing libraries to provide additional features like property-based testing, Snapshot Testing, mocking, and more.
In this blog post, we're going to explore some of the most popular testing libraries in Rust, it's important that you know that these libraries exist even if you don't use them right away, as they can be very useful in certain situations and when you detect those situations you'll know exactly what to do.
So, let's get started!
Property-Based Testing
Property-based testing is a testing approach that generates random inputs to test the properties of a program instead of writing specific test cases. It makes it easier to write exhaustive tests and finding edge cases that might be missed by traditional unit testing.
Here are some of the most popular property-based testing libraries in Rust:
Proptest
The proptest
crate is a property-based testing framework for Rust, inspired by the Hypothesis framework for Python.
Property-based testing is basically a testing approach that generates random inputs to test the properties of a program instead of writing specific test cases.
proptest
allows developers to test their code by generating random inputs and checking that certain properties hold for all inputs. If a failure is found, proptest
automatically shrinks the input to find the minimal failing test case.
Let's demonstrate that in an example.
Proptest Example
Let's say we have a function parse_date()
and we want to write extensive tests for it.
Without proptest
, we would have to write individual test cases for different dates, like this:
While this can be useful, it's not exhaustive and doesn't cover all possible edge cases, and definitely we're not going to write all the possible combinations in the universe in our test cases.
Instead of writing individual test cases, we can use proptest
to generate random dates by itself, without us having to do it manually.
Here's an example of using proptest
to test a simple date parsing function:
Syntax
Here's how the syntax works:
proptest!
is a macro that defines a property-based test.0u32..10000
generates a randomu32
value between0
and9999
.1u32..13
generates a randomu32
value between1
and12
.1u32..32
generates a randomu32
value between1
and31
.
This code generates all possible combinations of y
, m
, and d
in the specified ranges and tests that the parsed date is equal to the original date.
proptest
is designed to complement traditional unit testing by automatically generating a wide range of test inputs. It is particularly useful for uncovering edge cases that may not be immediately obvious.
Proptest Limitations
- Performance: Generating complex values in
proptest
can be slower compared to other property testing frameworks like QuickCheck, due to its richer shrinking model. - Edge Case Coverage: Property testing is unlikely to find specific edge cases in large input spaces without extensive testing time.
proptest
is a great tool for you to use to enhance your testing strategy with property-based testing. It offers powerful capabilities to test code against a wide range of inputs making exhaustive tests easier to write.
QuickCheck
QuickCheck just like proptest
is a property-based testing framework. It allows for testing functions by generating random inputs and checking that a specified property holds for all inputs.
If the property fails, quickcheck
automatically shrinks the input to find the minimal counterexample that causes the failure.
QuickCheck Example
Here's a simple example testing a function that reverses a vector:
This example uses the quickcheck!
macro to verify that reversing a vector twice results in the original vector.
Comparison with proptest
While quickcheck
is efficient and straightforward, proptest
offers more advanced shrinking capabilities and finer control over input generation. If shrinking behavior in quickcheck
is a concern, consider using proptest
as an alternative.
quickcheck
is an excellent tool for introducing property-based testing into Rust projects, making it easier to find and debug edge cases that might be missed by traditional unit testing.
Snapshot Testing
Snapshot Testing is a testing technique that captures the output of a function and compares it to a previously stored snapshot. If the output changes, the test fails, and the developer must decide whether to update the snapshot or investigate the change.
Snapshot testing is particularly useful when testing complex data structures or large outputs that are difficult to verify manually.
Here are some popular snapshot testing libraries in Rust:
Insta
The insta
crate is a snapshot testing library for Rust. It allows developers to perform snapshot tests, which are useful for comparing complex values against reference snapshots, especially when those values are large or frequently changing.
Insta Example
Le'ts say we have a function that needs to return a large array of numbers in a specific order, and we want to test it. Here's what we have to do:
- Define your function.
- Write the snapshot testing for the function.
- Run the test for the first time (it will generate a new snapshot).
- Review the snapshot and accept it.
- Run the test again, this time it will compare the new output with the snapshot that has been saved, if they match, the test is marked as passed, and if they differ, the test is marked as failed.
Here's a basic example of how to use insta
for snapshot testing, let's define the function first, we'll define a simple function that returns a vector of numbers from 1
to 100
:
Now, if we write the test, it will not be convenient to write whe whole expected vector in the test case, so we can use insta
to generate a snapshot for us.
This is doable but extremely inconvenient and in some situations impossible:
Instead, we can use insta
to generate the snapshots for us, here's how to do it:
Run the test, this will generate the snapshot for the first time but will fail the test until you review the snapshot and approve it:
Insta Snapshot
Insta tells us to run the command cargo insta review
to review all new snapshots, to do that, you must first install the cargo-insta tool:
Or, it's best if you visit their GitHub instructions page.
After installation, run the review command:
This will give you the option to accept, or reject the snapshot, if you accept it, the subsequent test runs will compare against the snapshot you've just accepted.
Insta Snapshot Review
Insta Features
This is just tip of the iceberg, insta
has many features that make it a powerful snapshot testing library:
- Inline Snapshots: Snapshots can be stored directly in the source file with the help of the
cargo-insta
tool. - Editor Support: There's a VSCode extension for viewing and managing
.snap
files. - Diffing: Uses the
similar
crate for diffing operations, which can also be used independently withsimilar-asserts
.
insta
is widely adopted and provides powerful tools for maintaining accurate and up-to-date tests, making it a valuable asset for Rust developers who need precise and comprehensive snapshot testing capabilities.
CLI Testing
Command-line interfaces (CLIs) also need to be tested, testing them manually can be cumbersome and error-prone. Here are some libraries that can help you test your CLI applications:
assert_cmd
assert_cmd
is a Rust crate designed to simplify integration testing of CLI applications, it achieves this by:
- Finding the binary of the crate to test.
- Asserting the success or failure of the program's execution.
You can easily test your CLI applications, here's an example:
In this test, we are expecting the command my-cli-app
to run successfully and return an exit code of 0
.
There is plenty of other things you can do with it, read the documentation to learn more about it.
Conclusion
In this blog post, we've explored some of the most popular testing libraries in Rust, including property-based testing with proptest
and quickcheck
, snapshot testing with insta
, and CLI testing with assert_cmd
.
Testing is an essential part of developing robust software, and these libraries can contribute to your testing strategy by providing additional features and testing strategies that complement traditional unit testing.
Thank you for reading! If you enjoyed this blog post, you might also like to try out our Rust practice section where you can solve Rust exercises and learn by doing.
Good luck with your Rust testing journey! 🦀🧪