Structure for applications to support integration tests

If I have the application project structure:

.
├── Cargo.lock
├── Cargo.toml
├── README.md
├── src
│   ├── arcam_protocol.rs
│   └── main.rs
└── tests
    ├── integration_tests.rs
    └── mock_avr850.rs

it seems that the module arcam_protocol cannot be used in integration_tests or mock_avr850. If main.rs is lib.rs, i.e. the project is a library then arcam_protocol can be used in both the files of the integration tests. Is there any way of exporting modules from an application so they can be used in integration tests code?

You can't use an executable (i.e. something with src/main.rs) as a library because then whichever program tries to use your library would end up with two main() functions.

To expose a module so another crate can use it (e.g. your integration tests) you should use pub mod when declaring the module.

// src/lib.rs

// tell the compiler that `lib.rs` contains an `arcam_protocol` module. The `pub` bit 
// makes it publicly accessible.
pub mod arcam_protocol;

Then your integration test would use it with something like this:

use my_awesome_crate::arcam_protocol;

#[test]
fn foo() {
    ...
}
1 Like

This appears to be forcing me to create two crates one for the protocol library and one for the actual GTK+ application so as to be able to do integration tests of the protocol code. I can live with this, it just seems awkward compared to what can be done with Python, or D.

The option of the crate being a library with an application in src/bin doesn't really work as that means all the client code has to be in a single file.

One option would be to keep the main.rs as-is, and add a lib.rs side-by-side:

.
├── Cargo.lock
├── Cargo.toml
├── README.md
├── src
│   ├── arcam_protocol.rs
│   ├── lib.rs
│   └── main.rs
└── tests
    ├── integration_tests.rs
    └── mock_avr850.rs

The integration tests will then be able to test the lib's public interface, without any need to change how main.rs is set up.

If you did want to publish this as a library in future, it would be nice for the library to have its own Cargo.toml file so that it doesn't need to depend on crates that only the binary uses, like argument parsing crates for example. You could do that using a Cargo workspace to contain separate library and binary crates.

Another thought is that it is possible for binaries in src/bin to be made up of multiple files, this works for example:

.
├── Cargo.lock
├── Cargo.toml
├── README.md
├── src
│   ├── bin
│   │   └── my-binary
│   │       ├── foo.rs
│   │       └── main.rs
│   ├── arcam_protocol.rs
│   └── lib.rs
└── tests
    ├── integration_tests.rs
    └── mock_avr850.rs

There can be multiple folders alongside my-binary too. main.rs can declare child modules as normal:

mod foo;

Though if there was much complexity there, I'd probably still opt for using a workspace myself.

1 Like