Cargo project structure. Best practices for unit tests/integration tests?


#1

Hello, I have more then one question, so I’ve decided to start discussion here:

I’ve pushed my project to github (yeap, it’s url shortener one more time) -> https://github.com/raventid/coursera_learning/tree/master/url_shortener
to make a few references here.

I followed a pretty straightforward approach, in my opinion, and organized my project like this:
I’ve created file tests.rs to put my integration tests there and file main.rs to put my code in it. And successfully runned tests with cargo test. BTW rocket app started successfully too and I was pretty happy.

Then I’ve decided to create folder storage for storage adapters, like redis or other stuff, and write unit tests for them right in the according files.
I’ve tried to run my storage units with cargo test storage (and a few more times storage/tests storage/tests.rs storage/* storage/please) and failed.

Then I’ve tried to move my tests.rs to tests/integration_tests.rs. And they stopped to work) I found a note that my integration tests in separate folder are external crates, but I was not able to use extern crate url_shortener here(is it right?). -> https://github.com/raventid/coursera_learning/blob/master/url_shortener/tests/integration_test.rs#L1

I’ve been looking at diesel-rs and servo projects and a few more to see how other people organize their projects. As I understand right now I should organize each folder
as a separate package (I’m not sure about “package” term, but I mean “package” word from Cargo.toml). To me it looks weird and feels like puting Gemfile in each folder :slight_smile:
Latter, I found crossbeam project, they have unit tests in separate files and only root Cargo.toml for project. So I’m confused, how they run this tests?

So my questions are:

  1. Should I create Cargo.toml file for each subfolder of my project and run specs inside of this subfolder, is it default project structure?
  2. How to run unit tests for specific file in specific folder, i.e. cargo run /lib/users/credit_cards.rs? I imagine this like wrapping particular file with a main function and running tests against that.
  3. Should I do something with my Cargo.toml to use extern crate url_shortener in my integration tests, when I put them in separate tests folder?

And I’m not yet confident about my understanding what crate is and how to deal with Rust modularity, so perhaps I googled my problems the wrong way :slight_smile:


#2

I wouldn’t take Servo’s test structure as a good model here - it’s split up the way it is because some of its crates take 5-10 minutes to build, so making changes to test code was a very frustrating experience.


#3

If you’re components are big enough and reusable, do it. In this case, I don’t think it’s worth the hassle.

edit: see @matklad’s response below (cargo test -- foo::bar::baz)

I don’t think it’s possible to test only a submodule. Your options are (apart from splitting the project into multiple packaces):

  • Move the submodule’s tests to a file in a tests directory, so you can run it with cargo test --test name. Please note that this way, you can only test public API of the crate. Unit-tests of the private methods has to be declared in the module (or a submodule of that module).
  • Use a consistent naming scheme (eg. submodule_testname), so you can filter tests by a test name: cargo test -- submodule_

Not exactly in Cargo.toml, but you’ll need to modify your project’s structure. The extern crate url_shortener imports a crate from src/lib.rs. Since you don’t have lib.rs, it doesn’t work.

The usual layout for applications is to have both main.rs and lib.rs and let the main.rs be only a thin wrapper over lib.rs (in main.rs, you also use extern crate url_shortener).


#4

cargo test foo::bar::baz will run all tests whose name contains foo::bar::baz which is usually a good enough approximation.


#5

If you split your project into multiple packages (crates), then you can run all tests from all crates in a workspace with

cargo test --all