» Quick Introduction to Rust » 2. Advanced » 2.7 Testing

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]);
    }
}