Tests
Tests are Rust functions that verify that the non-test code is functioning in the expected manner. The bodies of test functions typically perform these three actions:
Set up any needed data or state.
Run the code you want to test.
Assert that the results are what you expect.
The Rust community thinks about tests in terms of two main categories: unit tests and integration tests.
Unit Test
Unit tests are small and more focused, testing one module in isolation at a time, and can test private interfaces. The purpose of unit tests is to test each unit of code in isolation from the rest of the code to quickly pinpoint where code is and isn’t working as expected.
The convention is to create a module named tests
in each file to contain the test functions that annotated with #[test]
and to annotate the module with cfg(test)
.
#[cfg(test)]
annotation on the tests
module tells Rust to compile and run the test code only when we run cargo test
not when we run cargo build
. This saves compile time and saves space in the compiled artifact when running cargo build
.
Example:
Run the test using cargo test
.
Integration Test
Integration tests are entirely external to your library and use your code in the same way any other external code would, using only the public interface and potentially exercising multiple modules per test.
To create integration tests, you first need a tests
directory at the top level of our project directory, next to src
. Cargo knows to look for integration test files in this directory. We can then make as many test files as we want, and Cargo will compile each of the files as an individual crate.
Lets add this code into src/lib.rs
:
And this integration test code in test/integration_test.rs
:
Because of my project folder is named
rplay
we import it usinguse rplay::adder;
. So please adjust it using your own project name.
Run the test using cargo test
.
Assertion
In general there are 3 types of assertion provided by Rust which is: assert!
, assert_eq!
, and assert_ne!
. But we can combine it with different macros and function to create better test cases.
assert!
assert!
Checks if the condition is
true
.Fails if
false
.
assert_eq!
assert_eq!
Checks if the given two values are equal.
Fails if not equal.
assert_ne!
assert_ne!
Checks if the two values are not equal.
Fails if equal.
Custom Panic Message
We can also call panic!
explicitly if needed to make the test fail.
Additionally all the assertion macros has a second form, where a custom panic message can be provided with or without arguments for formatting.
And if failed will produce output like this:
Expecting Panic
We can use #[should_panic]
annotation if expect the test will result panic. For example check if a function panics as expected:
Commonly Paired Functions for Assertions
Use other assert-like macros and functions that are frequently used in Rust to validate conditions during testing or debugging.
matches!
: Validates if a value matches a specific pattern.
Validation Functions: .is_empty()
/ .is_none()
/ .is_some()
/ .is_ok()
/ .is_err()
Iterator Functions: .any()
/ .all()
/ .count()
Regular Expression: regex::Regex
Install it in your project using
cargo add regex
.
Best Practices
Use
assert_eq!
orassert_ne!
: They provide better error messages than plain assert! for equality checks.Leverage
matches!
for Pattern Matching: Cleaner and more expressive than manual matching.Use Descriptive Test Names: Explain what is being tested and under what conditions.
Use Custom Panic Messages: Add a helpful message to assertions.
Keep Assertions Focused: Ensure each test checks a single condition or small, related conditions.
References
Last updated