Testing
Rust includes support for writing tests within the language itself.
Unit Testing
Tests are functions that verify that the business code is functioning as expected.
Most unit tests go into a tests
mod with the #[cfg(test)]
attribute. Test functions are marked with the #[test]
attribute.
// Import the module you want to test
use crate::my_module;
#[cfg(test)]
mod tests {
// Import necessary items for testing
use super::*;
// A unit test for the add function
#[test]
fn test_add() {
let a = 3;
let b = 4;
let result = my_module::add(a, b);
// Assert: check the result
assert_eq!(result, 7);
}
// More tests here
}
Test can be run with cargo test
.
$ cargo test
running tests
...
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Testing panics
To check functions that should panic under certain circumstances, use attribute #[should_panic]
.
pub fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Cannot divide by zero!");
}
a / b
}
#[cfg(test)]
mod tests {
use super::*;
// A test using should_panic to check for a panic
#[test]
#[should_panic]
fn test_divide_by_zero() {
// This call is expected to panic
divide(10, 0);
}
}
Running Specific Tests
To run specific tests one may specify the test name to cargo test
command.
$ cargo test test_divide_by_zero
Tests can be marked with the #[ignore]
attribute to exclude some tests.
#[test]
#[ignore]
fn ignored_test() {
assert_eq!(divide(58, 2), 29);
}
Doc Testing
The primary way of documenting a Rust project is through annotating the source code.
/// Adds two numbers and returns the result.
///
/// # Examples
///
/// `` `
/// let result = my_module::add(3, 4);
/// assert_eq!(result, 7);
/// `` `
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Code blocks in documentation are automatically tested when running the regular cargo test
command.
Or, run the doc tests specifically with cargo test --doc
.
Integration Testing
Unit tests are testing one module in isolation at a time: they're small and can test private code. Integration tests are external to your crate and use only its public interface in the same way any other code would.
You can create a separate integration test file named tests/integration_test.rs
, and then run the command cargo test
.
$ cargo test
Compiling my_project v0.1.0 (/path/to/my_project)
Finished test [unoptimized + debuginfo] target(s) in 0.71s
Running target/debug/deps/my_project-<hash>
running 2 tests
test unit_test ... ok
test doc_test_placeholder ... ok
test integration_test ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Dev Dependencies
Sometimes there is a need to have dependencies for tests only.
Such dependencies are added to Cargo.toml
in the [dev-dependencies]
section.
[dev-dependencies]
pretty_assertions = "1"
And then in the test code, you can do this:
#[cfg(test)]
mod tests {
// Import the macros from pretty_assertions
use pretty_assertions::{assert_eq, assert_ne};
#[test]
fn test_example() {
let expected = vec![1, 2, 3];
let actual = vec![1, 2, 4];
assert_eq!(expected, actual);
assert_ne!(expected, vec![7, 8, 9]);
}
}