Writing rust automated tests
// add new module with test specific annotations
#[cfg(test)]
mod tests {
#[test]
fn exploration () {
assert_eq! ( 2 + 2 , 4 );
}
}
# running test with the following:
cargo test
#[derive( Debug )]
struct Rectangle {
width : u32 ,
height : u32 ,
}
impl Rectangle {
fn can_hold ( & self , other : & Rectangle ) -> bool {
self . width > other . width && self . height > other . height
}
}
#[cfg(test)]
mod tests {
use super ::* ;
#[test]
fn larger_can_hold_smaller () {
let larger = Rectangle {
width : 8 ,
height : 7 ,
};
let smaller = Rectangle {
width : 5 ,
height : 1 ,
};
// checking results with the `assert!` macro
assert! (larger . can_hold ( & smaller));
}
}
pub fn greeting (name : & str ) -> String {
String :: from ( "Hello!" )
}
#[cfg(test)]
mod tests {
use super ::* ;
#[test]
fn greeting_contains_name () {
let result = greeting ( "Carol" );
// adding custom failure messages
assert! (
result . contains ( "Carol" ),
"Greeting did not contain name, value was `{}`" ,
result
);
}
}
pub struct Guess {
value : i32 ,
}
impl Guess {
pub fn new (value : i32 ) -> Guess {
if value < 1 {
panic! (
"Guess value must be greater than or equal to 1, got {}." ,
value
);
} else if value > 100 {
panic! (
"Guess value must be less than or equal to 100, got {}." ,
value
);
}
Guess { value }
}
}
#[cfg(test)]
mod tests {
use super ::* ;
// checking panics with `should_panic`
#[test]
#[should_panic(expected = "Guess value must be less than or equal to 100" )]
fn greater_than_100 () {
Guess :: new ( 200 );
}
}
#[cfg(test)]
mod tests {
// using Result<T, E> in Tests
#[test]
fn it_works () -> Result <(), String > {
if 2 + 2 == 4 {
Ok (())
} else {
Err ( String :: from ( "two plus two does not equal four" ))
}
}
}
Controlling how tests are run
# no parallelism test execution
cargo test -- --test-threads=1
# display printed values for passing tests as well
cargo test -- --show-output
Using the following Rust code:
pub fn add_two (a : i32 ) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super ::* ;
#[test]
fn add_two_and_two () {
assert_eq! ( 4 , add_two ( 2 ));
}
#[test]
fn add_three_and_two () {
assert_eq! ( 5 , add_two ( 3 ));
}
#[test]
fn one_hundred () {
assert_eq! ( 102 , add_two ( 100 ));
}
}
# running a single test using the test method name
cargo test one_hundred
# running multiple tests by specifying part of a test name
cargo test add
#[test]
fn it_works () {
assert_eq! ( 2 + 2 , 4 );
}
// ignoring test with ignore annotation
#[test]
#[ignore]
fn expensive_test () {
// code that takes an hour to run
}
Test organization
Unit tests
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.
You’ll put unit tests in the src
directory in each file with the code that
they’re testing. The convention is to create a module named tests in each file
to contain the test functions and to annotate the module with cfg(test)
.
The #[cfg(test)]
annotation on the tests module tells Rust to compile and run
the test code only when you run cargo test, not when you run cargo build. This
saves compile time when you only want to build the library and saves space in the
resulting compiled artifact because the tests are not included.
Testing private functions
pub fn add_two (a : i32 ) -> i32 {
internal_adder (a, 2 )
}
fn internal_adder (a : i32 , b : i32 ) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super ::* ;
#[test]
fn internal () {
// possible to test private functions
assert_eq! ( 4 , internal_adder ( 2 , 2 ));
}
}
Integration tests
Integration tests are in another directory:
$ tree
├── src
│ └── lib.rs
└── tests
└── integration_test.rs
tests/integration_test.rs:
use adder;
#[test]
fn it_adds_two () {
assert_eq! ( 4 , adder :: add_two ( 2 ));
}
# run all the tests in a particular integration test file:
cargo test --test integration_test
Submodules in integration tests
$ tree
├── src
│ └── lib.rs
└── tests
├── common
│ └── mod.rs
└── integration_test.rs
// tests/common/mod.rs
pub fn setup () {
// setup code specific to your library's tests would go here
}
// tests/integration_test.rs
use adder;
mod common ;
#[test]
fn it_adds_two () {
common :: setup ();
assert_eq! ( 4 , adder :: add_two ( 2 ));
}