# How to write tests 1 - Setup data or state 2 - Run the code to test 3 - Assert that result match expectations ## Anatomy of a Test function function + `test` attribute Attributes are metadata about parts of code, like `derive`. ```rust #[test] // Makes the function a test one fn somefun(){} ``` Test functions reports its result. On libraries, a test modules is automatically generated ```rust #[cfg(test)] mod tests { #[test] fn it_works() { let result = 2 + 2; assert_eq!(result, 4); } } ``` `#[test]` Indicates that such function must be run standalone per test, you can have functions that don't have it and can be used inside test functions `assert_eq!()` is a macro that checks that a value is the same as the one passed `cargo test` to run the tests of a project ```rust running x test test tests::it_works ... ok // Test of function it_works from module tests, passed //more tests... test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s // Results of a test ``` Then it runs `Doc-tests` -> Documentation tests That would be compiling the code example in an API documentation (Chapter 14) When a test fail for example: ```rust #[test] fn another_fail() {panic!("Test fails 100%");} ``` Then the test shows as a failure and marks which tests failed! ```rust failures: tests::another_fail test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` ## Adding functionality to test To add usage of other types and functions, inside the module we have to include the namespaces of what we want to use (Chapter 7). `use super::*` -> Include all in the parent scope of the module we are (test). ## `assert!` Macro Checks that a condition that evaluates to a `bool`. On a `false` it panics. ### `assert_eq!` and `assert_ne!` It is basically the same but you pass the two values you want to compare: ```rust assert_eq!(result, expected); // macro does: result == expected assert_ne!(result, expected); // macro does: result != expected ``` The cool part is that it gives more information about the values used, ex: ```rust pub fn add_two(a: i32) -> i32 { // Add two but buggy because it adds 3 a + 3 } //... #[test] fn it_adds_two() { // Check that 2+2 = 4, and add_two(2) = 4 too assert_eq!(4, add_two(2)); } //... ---- tests::it_adds_two stdout ---- thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)` left: `4`, right: `5`', src\lib.rs:58:24 ``` For the usage of these 2 macros, the `Type` must implement the traits `PartialEq` and `Debug` as it is using comparison and printing debug information. `#[derive(PartialEq, Debug)]` -> more in Appendix C - Derivable Traits ### Custom Failure Messages to these macros Any argument we pass after the needed ones, will passed to a `fmt` to format the values into a string, ex: ```rust pub fn greeting(name: &str) -> String { String::from("Hello!") // Does not contain name to cause bug } //... #[test] fn greeting_contains_name() { let result = greeting("Carol"); assert!( result.contains("Carol"), "Greeting did not contain name, value was `{}`", result ); } //... ---- tests::greeting_contains_name stdout ---- thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain name, value was `Hello!`', src\lib.rs:67:9 ``` If there were no formatted string passed, it would have only told use that it failed and at which line. ## `should_panic` - Check for Panics `#[should_panic]` Attribute that tells that a function should throw a `panic!` ```rust pub struct Guess { value: i32, } impl Guess { pub fn new(value: i32) -> Guess { if value < 1 { // We are not checking if value > 100 panic!("Guess value must be between 1 and 100, got {}.", value); } Guess { value } } } //... #[test] #[should_panic] fn greater_than_100() { Guess::new(200); // value > 100, we expect the function to panic! } //... ---- tests::greater_than_100 stdout ---- note: test did not panic as expected // new() does nto check v > 100, so it does not panic as expected - Fail! ``` But the message is not very descriptive, that can be solved using the `expected` parameter of `should_panic`: ```rust #[should_panic(expected = "Message clarifying what is the expected behaviour")] ``` This message should be the final expected panic message, if the actual panic message differs from the expected it will be counted as a failure, showing both messages. ## Using `Result` in Tests Instead of throwing `panic!`, we can use the `Result` to throw `Ok` or `Err` depending on success or failure. Can't use `#[should_panic]` with `Result`, return `Err` should the test fail. ```rust #[test] fn it_works() -> Result<(), String> { if 2 + 2 == 4 { Ok(()) } else { Err(String::from("two plus two does not equal four")) } } ```