Unit tests "tests" module

Is the module tests mandatory, good practice or optional for unit tests ?

References

Let's start with some references:

  • Rust programming language book
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

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. You’ll see that because integration tests go in a different directory, they don’t need the #[cfg(test)] annotation. However, because unit tests go in the same files as the code, you’ll use #[cfg(test)] to specify that they shouldn’t be included in the compiled result.

Example

fn add_two(int: u32) -> u32 {
    int + 2
}

// Proposition 1 
#[test]
fn add_two_test() {
    assert_eq!(add_two(2), 2 + 2);
}

// Proposition 2 
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_two_test() {
        assert_eq!(add_two(2), 2 + 2);
    }
}

Relevant questions

  • Does rustc compile (cargo check/build) my functions annoted with #[test] when these functions are not defined in a #[cfg(test)] annoted module ? (like in proposition 1)
  • Is there any advantage for proposition 1 over proposition 2 and vice-versa ?
  • Why does the rust programming language book recommend proposition 2 ?
  • The community has adopted proposition 2 a long time ago (see link to discourse topic) should I use the same guideline ?

Remarks

  • Proposition 1 at first sight is more straightforward
  • Proposition 2 prevents namespace pollution for the current module

Thanks

The compiler will only compile things annotated with #[test] when running tests, but what if you have a helper function for your tests? The helper function would always be compiled when using proposition 1. Similarly, if you have imports only used by tests, then proposition 1 will emit unused warnings on them when not compiling for tests.

use std::cmp::min;

fn add_two(int: u32) -> u32 {
    int + 2
}

// Proposition 1 
#[test]
fn add_two_test() {
    assert_eq!(add_two(2), min(4, 6));
}
warning: unused import: `std::cmp::min`
 --> src/lib.rs:1:5
  |
1 | use std::cmp::min;
  |     ^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default
1 Like

Thanks for pointing me to limitations I did not see.

  • Helper functions is a real problem and only proposition 2 can prevents compilation
  • Imports can be included directly inside the test function but it can be repetitive and cumbersome when the number of tests increase

With these points you highlighted, tests module seems to be highly recommended! :wave:
Thanks

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.