Integration Testing

Shared Test Utilities

When multiple integration test files in tests/ need common setup helpers, mock servers, or sample data builders, those shared test utilities must be placed inside a subdirectory module (such as tests/common/mod.rs) rather than a top-level tests/common.rs file.

Why tests/common.rs Causes Unwanted Empty Test Runs

If you create tests/common.rs directly inside tests/, Cargo treats common.rs as an independent integration test binary with zero #[test] functions, printing an empty Running tests/common.rs (0 tests) section on every cargo test run.

Placing the shared module inside a subdirectory—tests/common/mod.rs—tells Cargo not to compile common as a standalone test target:

Correct Layout for Shared Utilities:
tests/
├── common/
│   └── mod.rs         <-- Subdirectory: NOT compiled as a standalone binary
├── client_tests.rs    <-- Declares `mod common;`
└── server_tests.rs    <-- Declares `mod common;`

Suppressing False-Positive dead_code Warnings in tests/common/mod.rs

When both tests/client_tests.rs and tests/server_tests.rs write mod common;, each integration test binary compiles its own copy of common. If common defines setup_client() (used only by client_tests.rs) and setup_server() (used only by server_tests.rs), the compiler will emit a warning: function setup_server is never used while compiling client_tests.rs.

There are two clean solutions:

  1. Add #![allow(dead_code)] at the top of tests/common/mod.rs.
  2. Consolidate your integration tests into a single top-level tests/integration.rs binary (as shown in Section 54.4) so all submodules share the same crate compilation unit.
// File: tests/common/mod.rs
pub mod common {
    #![allow(dead_code)]

    #[derive(Debug)]
    pub struct TestAccount {
        pub username: String,
        pub api_key: String,
    }

    pub fn fixture_account(name: &str) -> TestAccount {
        TestAccount {
            username: name.to_string(),
            api_key: format!("test_key_{name}_99"),
        }
    }

    pub fn unused_in_this_binary_helper() -> u32 {
        42
    }
}

fn main() {
    let acct = common::fixture_account("alice");
    println!("Created shared fixture for {}: {}", acct.username, acct.api_key);
}
Created shared fixture for alice: test_key_alice_99

Sharing Utilities Between Unit Tests (src/) and Integration Tests (tests/)

A unit test module (#[cfg(test)] mod tests inside src/lib.rs) cannot import tests/common/mod.rs because src/lib.rs is compiled before tests/. If you need the exact same test builders or mock traits in both unit tests (src/) and integration tests (tests/), you have two options:

  • Workspace internal test crate: Create a small workspace member crate (e.g., crates/test_utils) and list it under [dev-dependencies] in Cargo.toml. Both src/ unit tests and tests/ integration tests can import test_utils::*.
  • Feature-gated test-util module: Expose a pub mod test_util in src/lib.rs guarded by #[cfg(any(test, feature = "test-util"))].