Testing in Rust: Frameworks and Tools
Rust is a systems programming language that places a huge emphasis on code reliability and safety. The built-in testing system allows developers to write unit tests, integration tests, and documentation tests without installing additional libraries. However, for complex scenarios — parameterization, random data generation, or mocking — the Rust community has created powerful frameworks. In this article, we will break down the key testing tools in Rust.
Built-in test Module
Rust comes with a built-in test module, which is activated via the #[test] attribute. The cargo test command is used to run tests. Example of a simple unit test:
// src/lib.rspub fn add(a: i32, b: i32) -> i32 { a + b}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_add() { assert_eq!(add(2, 2), 4); }
#[test] fn test_add_negative() { assert_eq!(add(-1, 1), 0); }}Key features of the built-in module:
- Attributes:
#[test],#[ignore],#[should_panic]. - Assertion macros:
assert!,assert_eq!,assert_ne!. - Integration tests: placed in the
tests/directory and test the public API. - Documentation tests: code examples in doc-comments, verified via
cargo test.
For most projects, this is sufficient. But when parameterization, random data generation, or mocking is required, specialized frameworks come to the rescue.
rstest Framework — Parameterized Tests
The rstest library provides a powerful mechanism for test parameterization, similar to pytest in Python. It allows you to specify multiple sets of input data and expected results without code duplication.
Installation:
[dev-dependencies]rstest = "0.22"Example of a parameterized test:
use rstest::rstest;
#[rstest]#[case(1, 2, 3)]#[case(0, 0, 0)]#[case(-1, 1, 0)]fn test_add(#[case] a: i32, #[case] b: i32, #[case] expected: i32) { assert_eq!(add(a, b), expected);}Capabilities of rstest:
- Parameterization via
#[case]. - Random data generation via
#[random]. - Contextual fixtures via
#[fixture]. - Support for asynchronous tests.
This framework is especially useful when testing mathematical functions, parsers, or algorithms where many edge cases need to be checked.
Property-based Testing with proptest
Property-based testing is a method where you describe invariants (properties) that must hold for all possible input data. The proptest library generates random input data and checks these properties.
Installation:
[dev-dependencies]proptest = "1.5"Example: verifying that sorting preserves length and all elements:
use proptest::prelude::*;
proptest! { #[test] fn test_sort_keeps_elements(mut vec: Vec<i32>) { let original_len = vec.len(); let mut sorted = vec.clone(); sorted.sort(); assert_eq!(sorted.len(), original_len); for item in &vec { assert!(sorted.contains(item)); } }}Advantages of proptest:
- Automatic generation of thousands of random scenarios.
- Shrinking — when a test fails, the library tries to find the minimal failing input.
Also in library
Symfony/Mailer for PHP: Complete Guide with Examples
Working with YAML in Go (Golang): go-yaml and yaml.v3 libraries
Working with Excel in C# (.NET): EPPlus and ClosedXML Libraries
Rate Limiting in Go (Golang): Libraries, Patterns, and Implementation Examples
Working with Images: resize, crop in TypeScript