Hacker News new | ask | show | jobs
by yarper 3400 days ago
After writing Rust in production for a while, the biggest bugbear I have is the naming/file structure.

I end up a lot with this;

    src/main.rs
    src/combobulator/mod.rs
    src/combobulator/tests.rs
    src/tests.rs
    src/somethingelse/tests.rs
    src/somethingelse/mod.rs
Because I find tests in the same file a bit confusing. It's really easy with maven-style layouts to know that "only things in main/java or main/scala get compiled and go into the jar". "src/test/*" and "src/main/resources" are for me. The same thing applies for cargo.tomls and resources - there's not really a way to see what goes into the executable from the file structure.

But this isn't the biggest problem with having things called "mod.rs". That would be if I open 5 mod.rs's in a text editor with tabs, I have no idea what goes with what.

I know that tests should go under tests/, but that's specifically for integration tests. Integration tests are an order of magnitude less likely to get written imo, and if they are they'll probably get written as unit tests anyway.

If anyone has any top tips for how to structure larger Rust projects while separating unit tests into different files, please let me know!

4 comments

I prefer to keep tests in a `mod tests { ... }` block at the end of the source file, which provides comparable benefits and separation. I also prefer to use `combobulator.rs` rather than `combobulator/mod.rs`, for multiple reasons including filename ambiguity. In this case, you'd have:

    src/main.rs
    src/combobulator.rs
    src/somethingelse.rs
Too much code in one place. Makes it hard to read the file and difficult to see what is code and what are tests. `wc` can no longer give you a quick approximation of code size either.
In case you didn't see, I mentioned there is a way to do this right now:

https://news.ycombinator.com/item?id=13791945

So I can keep a separate src/ and tests/ hierarchy? Why are the rust people so insistent on this even though most seen to want to could their test and src trees.
I think this is a very common standard. At least, its what I have seen most often.
One approach for this that I haven't really seen get used much that you can use a macro to get around this. In your comboluator.rs, just add this at the bottom of the file:

    #[cfg(test)]
    Mod tests {
        include!("comboluator_tests.rs");
    }
Then write your tests there. It allows you to write unit tests against private things, but also allows you to decouple the files to keep things cleaner.
I have no tips to fix. When cargo went gold long ago I complained about this and I was told I'd get used to it. I never have. The test layout is painful and I really hope it gets changed or at least added. I want all my tests for a module in one place. I hope this stops being a problem.
Additionally tests in "tests/" are compiled to independent binaries on which the test suite is run on separately, which results into verbose and redundant output when ran.

Also if you keep your tests under "src/", you can't just stick them wherever you want; you need to have #[cfg(tests)] on every module declaration leading to the test function, otherwise the test is ignored silently. From time to time I find old tests that have not ran once because of a missing attribute somewhere upwards the module hierarchy.

Hm, cfg(tests) should only control if they're included in non-test builds; it shouldn't ignore them entirely.