Rust unit test layout
Jeff DeWall 3 min read February 18, 2023I've been succeeding in my fourth attempt at learning to use Rust. I come from mostly a C++ background, and I've struggled the first couple of times I've tried to pick up Rust, ultimately stopping the effort to work on the next shiny quarter.
I started porting over my Nintendo 6502 processor emulator from C++ as a larger project to play around with. One thing I had done in the C++ one is have tons of tests for the different instructions - checking clock cycles and internal register states for the emulated CPU. Most of these tests ended up split up across different files to keep the codebase manageable.
Unit Testing in Rust
In Rust, the guides all mention keeping your unit tests in the same file in a sub module declared in the file and marked as a test module with #[cfg(test)]
.
You can also have an integration tests folder that lives as a sibling folder to the src
folder, but there the idea is to handle the modules as black boxes without the ability to peek into the internals - which in some cases can be useful in unit tests.
Splitting your Rust Unit Tests into Multiple Files
It turns out to not be very difficult to split up your unit tests into separate files, but it took me a bit to figure out and thought it might useful to others. Here is what the layout of my dummy project looks like now:
.
├── Cargo.lock
├── Cargo.toml
├── src
│ ├── cpu.rs
│ ├── lib.rs
│ └── tests
│ ├── cpu_tests.rs
│ └── mod.rs
All I've done is move the tests module's code into the cpu_tests.rs
file in the subfolder tests
.
cpu_tests.rs
use crate::*;
I then have a tests/mod.rs
that includes all of the test files, tests/cpu_tests.rs
only in this case.
mod.rs
Then by including that module in the top-level lib.rs
and marking it as a test module, it will still get run as a unit test without cluttering up lib.rs itself.
lib.rs
And the results of running the unit tests when I do a cargo test
:
)
)
; ; ; ; ;
Conclusion
Rust has been a great language to dig into a bit deeper, since it has some nice defaults, built in unit testing and the package and project management through cargo gets rid of a lot of boiler plate that I've constantly needed to duplicate across project for C++.
There are still some areas where I'm learning how to structure my code in a way that feels the most manageable and comfortable to me. Given that the searches I did didn't show this solution published elsewhere, I hope others also find this useful.