Grouping tests without extra dependencies? [Solved]

#1

I have utils.rs with some functions. I want to test each function with some input and expected output, one assertion per test with a descriptive name. I could prefix tests with name of the function being tested, but would prefer some “standard” way of grouping/scoping the tests.

Something like this:

#[cfg(test)]
mod tests {
  mod split_arguments_tests {
    #[test]
    fn it_splits_arguments_by_space() { ... }
    fn it_supports_string_literals { ... }
  }
  mod some_other_function_tests { ... }
}

pub fn split_arguments(args: &str) -> Vec<String> { ... }
pub fn some_other_function() -> String { ... }

Does there exist some convention for grouping tests? I did some searching, and all I found was stainless. Was hoping to group tests without extra dependencies.

#2

There are two standard ways to do this:

//In utils.rs
#[cfg(test)]
mod tests {
    //Your tests
    #[test]
    fn first_util_test() { ... }
}
//utils2.rs
#[cfg(test)]
mod tests {
    //Your tests
    #[test]
    fn first_util2_test() { ... }
}

Or

// in <Cargo.toml Dir>/tests/util_test.rs
use my_crate::utils::*;
#[test]
fn my_integration_test() {
    //This is meant to test from the point of view from the crate user
}

Please take a look at this chapter of Rust by Example and its subchapters for a better understanding of how this works. Specifically unit testing and integration testing

#3

Hi @OptimisticPeach, thanks for the reply :slightly_smiling_face:

Actually, grouping with mod works just fine. Though, the function must be in scope, which I was not able to grok. Nested use super works out fine:

#[cfg(test)]
mod split_arguments_tests {
  mod split_by {
    use super::super::*;
    #[test]
    fn space() {
      assert_eq!(split_arguments("a b"), vec!["a", "b"]);
    }

    #[test]
    fn tab() {
      assert_eq!(split_arguments("a\tb"), vec!["a", "b"])
    }
  }
  mod trimming {
    use super::super::*;
    #[test]
    fn spaces() {
      assert_eq!(split_arguments(" a  b "), vec!["a", "b"]);
    }
  }
}

Maybe a more elegant way exists?

#4

Hmm perhaps using a use statement in split_argument_tests seeing as you’re doing a ::* import. Or, even a

use crate::path::to::your::module::*;

Statement would work.

#5

Regarding imports for test modules, I always "carry the use super::*;". It is a simple practice that always works and is robust to moving the mods around afterwards:

#[cfg(test)]
mod split_arguments_tests {
  use super::*;

  mod split_by {
    use super::*;

    #[test]
    fn space ()
    {
      assert_eq!(split_arguments("a b"), vec!["a", "b"]);
    }

    #[test]
    fn tab ()
    {
      assert_eq!(split_arguments("a\tb"), vec!["a", "b"])
    }
  }
  mod trimming {
    use super::*;

    #[test]
    fn spaces ()
    {
      assert_eq!(split_arguments(" a  b "), vec!["a", "b"]);
    }
  }
}
1 Like
#6

There’s no magic in the mod tests name. You can put as many test modules as you want under any given module. For instance, to keep all tests for a given function grouped, I might do:

pub fn double(x: u32) -> u32 {
    x.wrapping_mul(2)
}

#[cfg(test)]
mod double_tests {
    use super::double;
    #[test]
    fn double_zero() {
        assert_eq!(double(0), 0)
    }

    #[test]
    fn double_maxint() {
        assert_eq!(double(std::u32::MAX), std::u32::MAX - 1);
    }
}

pub fn triple(x: u32) -> u32 {
    x.wrapping_mul(3)
}

#[cfg(test)]
mod triple_tests {
    use super::triple;
    #[test]
    fn triple_zero() {
        assert_eq!(triple(0), 0)
    }

    #[test]
    fn triple_maxint() {
        assert_eq!(triple(std::u32::MAX), std::u32::MAX - 2);
    }
}

playground link

1 Like