ProgrammingRust Developer

Explain how 'unit tests' are implemented in Rust, how to organize them correctly, and what techniques ensure the reliability and readability of the test code?

Pass interviews with Hintsage AI assistant

Answer.

Unit testing in Rust is built into the language itself through built-in macros, special attributes, and the cargo infrastructure. Historically, in C and several other languages, testing is an external overlay, which leads to a discrepancy between the production code interface and the tests. In Rust, tests are compiled and run in the same environment as the main code, eliminating the problem of "works only in tests".

Problem: Tests can slow down builds, be uninformative, or poorly organized; also, poorly isolated tests hinder code maintenance and readability.

Solution: Tests are written as special functions marked with the #[test] attribute inside a module mod tests. All test code is compiled and run only with the cargo test key; it is excluded from the production build. Macros like assert_eq!, should_panic, and setup methods are used to enhance the efficiency and cleanliness of testing.

Example code:

pub fn add(a: i32, b: i32) -> i32 { a + b } #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(add(2, 2), 4); } }

Key features:

  • Tests are compiled only when built with cargo test, and do not appear in the release binary
  • Full integration into the language through attributes and macros
  • Easy organization of tests by modules; test functions can be public or private

Trick Questions.

Is it mandatory to place tests only in the nested module mod tests?

Not necessarily, but it is common for the isolation of tests and preventing leakage of test code into release. It also helps to use #[cfg(test)].

Can tests be run only on a specific module/file?

Yes, you can specify the test name or path to it using cargo test name.

Are private functions tested?

Yes, if the test module is defined within the same file and uses use super::*;, the tests have access to all internal functions of the file.

Common mistakes and anti-patterns

  • Lack of separation of test modules from code (without #[cfg(test)])
  • Code duplication or complex logic inside tests
  • Not using assert_eq! or overly complex checks

Real-life Example

Negative case

Tests are mixed with the main code, not hidden in #[cfg(test)], and use global variables for initialization.

Pros:

  • Rapid prototyping

Cons:

  • Test code goes into release
  • Tests break due to changes in code and are not isolated

Positive case

Tests are encapsulated in nested modules, using setup functions and assert_eq! macros for verification.

Pros:

  • Isolation of tests from production code
  • Fast and predictable test execution

Cons:

  • Requires discipline and proper file structure