Rust testing
Documentation
Running Tests
By default, tests run in parallel
cargo test # run all tests (unit/integration/doc) cargo test tests::add_two_positive_numbers # run specific test cargo test add # run all tests with the word 'add' in their name cargo test --test my_integration_test # run all tests in 'tests/my_integration_tests' cargo test -- --show-output # show printed output (in both passing/failed) cargo test -- --ignored # only run ignored tests
Test Types
Unit Tests
In rust, it's customary to define your tests in the same file as your code.
Define your tests
// src/lib.rs // real code fn add<T>(a: T, b: T) -> T { a + b } // test code #[cfg(test)] // tell rust not to compile unless testing mod tests { use super::*; // include parent module (our src!) #[test] fn add_two_positive_numbers() { let res = add(1, 2); assert_eq!(res, 3); } #[test] fn add_a_postive_and_negative_number() { let res = add(1, -2); assert_eq!(res, -1); } }cargo test # run testsIntegration Tests
Basics
Each integration test is it's own crate.
They are written in thetests/
directory.NOTE:
executable crates can't be imported, so they can't have integration tests.
the convention is to keep yourmain.rs
very simple, and test your executable'slib.rs
crate.// tests/add_integration_tests.rs use my_code::*; #[test] fn add_two_positive_numbers() { assert_eq!(add(1, 3), 4); }Setup/Teardown/Testlib
Setup/Teardown code is just regular functions.
You must add them using asome_module/mod.rs
so that it is not run as tests.my_project/ tests/ common/ mod.rs // <-- test lib code here add_integration_tests.rs// tests/common/mod.rs pub fn setup() { // ... }// tests/add_integration_tests.rs mod common; #[test] fn do_the_thing() { common::setup(); }DocComment Tests
TODO
Assertions
Some common assertions.
NOTE:
rust doesn't care what order assertion params are defined in.
there is not a convention for one side with expectaton and the other for actual.assert_eq!(result, expects); // assert equal assert_ne!(result, expects); // assert not equal assert_eq!(result, "reason", expects); // add a message to assertion assert!(result) // assert true assert!(!result) // assert falseYou can assert that code panics by assigning an attribute.
// assert any part of code panics #[test] #[should_panic] fn open_non_existant_file_should_panic() { // ... } // assert panics with message #[test] #[should_panic(expected = "foo is not a number")] fn open_non_existant_file_should_panic() { // ... }Test functions can also use the Result type.
#[test] fn open_non_existant_file_should_panic() -> Result<(), String> { if add(1, 3) == 4 { Ok(()) } else { Err("1 plus 3 should be 4".to_string()) } }
Ignore Tests
skip this test
#[test] #[ignore] fn open_non_existant_file_should_panic() { // ... }