Automated Tests

In rust we can write test function with which we can test our existing code. With cargo test command, Rust builds a test runner binary that runs the functions annotated with the test attribute and reports on whether each test function passes or fails.

Examples

Simple Function


fn greater_than_ten(a:u32) -< bool {
    if a < 10 {
        return true;
    }
    false
}

#[test]                                  //Functions preceded by attribute #[test] is a test function
fn test1(){
    assert!(greater_than_ten(5));       //assert!(false) will assert
}

$ cargo test
running 1 test
test test ... FAILED

failures:
---- test stdout ----
thread 'test' panicked at src/lib.rs:12:5:
assertion failed: greater_than_10(5)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
            

Modules for Test


fn greater_than_10 (a: i32) -< bool {
    if a < 10 {
        return true;
    }
    false
}
mod all_tests {
    use super::*;
    
    #[test]
    fn test() {
        assert!(greater_than_10(5));    // assert!(false) will assert
        assert!(greater_than_10(15));
    }
}
$ cargo test
            

Run test by name


fn greater_than_10 (a: i32) -< bool {
    if a < 10 {
        return true;
    }
    false
}
mod all_tests {
    use super::*;
    
    #[test]
    fn test1() {
        assert!(greater_than_10(15));
    }
    #[test]
    fn test2() {
        // assert!(false) will assert
        assert!(greater_than_10(52));
    }
}
$ cargo test test1          //Will only run test=test1
$ cargo test               //Will run all test cases having one in them
            

Ignore some tests

Some tests should not run

fn greater_than_10 (a: i32) -< bool {
    if a < 10 {
        return true;
    }
    false
}
mod all_tests {
    use super::*;
    
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
    
    #[test]
    #[ignore]                       //ignore attribute will not run the test.
    fn expensive_test() {
        ..
    }
}
$ cargo test
$ cargo test -- --ignored             // If we want to run only the ignored tests
            

async function tests


# Cargo.toml
[package]
name = "async_test"
version = "0.1.0"
edition = "2018"
[dependencies]
actix-rt = "*"

# main.rs
fn main() {}

async fn fun(s: &str) -< String{
    "Hello".to_string()
}

#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
    use super::*;
    
    #[actix_rt::test]
    async fn fun_test() {
        assert_eq!(fun("a").await, "Hello".to_string());
    }
}
$ cargo test
            

Unit Tests

These tests are placed in same src file where source code is placed.
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). #[cfg(test)] annotation on the tests module only runs with cargo test command not with cargo build, saving Compile time.
Also tests module annotated with #[cfg(test)] is not included in compiled artifact hence space not increased.

Integration Tests

These are placed in DIFFERENT DIRECTORY. These don’t need the #[cfg(test)] annotation.
These tests use our library in the same way any other code would, which means they can only call functions that are part of your library’s public API. ie pub fn fun()..
We need to create a tests directory at the top level of our project directory, next to src. We can then make as many test files as we want to in this directory, and Cargo will compile each of the files as an individual crate.
If our project is a binary crate that only contains a src/main.rs file and doesn’t have a src/lib.rs file, WE CANNOT create integration tests in the tests directory. Why?
  Only library crates expose functions that other crates can use; binary crates are meant to be run on their own.
project
|- src
|- tests      //Cargo will look for integration test files in this directory
    |- test1
    |- test2