Should unit tests really be put in the same file as the source?

The Rust Programming Language states the following:

You’ll put unit tests in the src directory in each file with the code that they’re testing. 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) .

The average loc per file in my project is pretty high, the amount of loc from unit tests is more than double that of the actual code. How should I proceed to enhance the maintainability and the readability of my code? Should I put the unit tests in a separate file?

Yes, it doesn't have to be an inline module, you can still make it a submodule that is a separate file. The following is a fairly common way of doing this:

src/
  ...
  ops.rs
  ops/
    tests.rs

And then in ops.rs you can have the following declaration.

#[cfg(test)]
mod tests;

You can also break up the tests module into multiple modules if it is getting large.

Note: if you are testing the public API of a library crate ("Integration tests" described later on that page in the book). It is common practice to put those tests in a tests/ folder at the same level as a src/ folder.

src/
  ...
  ops.rs
tests/
  ...
  ops.rs
4 Likes

A nice benefit of that is that it speeds up compilation times, because the compiler doesn't have to parse the tests :smiley:

2 Likes

I guess that begs the question... Does having a large file have a negative impact on readability?

It depends on your codebase, but have a look through your tests and see where most of the lines are going. It might be that you are spending loads of time with initialization logic, in which case you could introduce helper functions to do the setup or refactor your code to be more decoupled and have less dependencies. For examle, often when I'm writing a parser I'll have loads of similar test cases checking for different bits of syntax so I'll pull the test code out into a macro.

You could also argue that your module is doing too much. Often when prototyping I'll write everything in one big module and then as I start to get a feel for the code's structure and flow I'll split it out into submodules for each logical chunk. It's similar to the idea of a god class where a single type may become bloated because it does too much.

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.