Skip to content

Integration tests

A crate integration test exercises the crate’s public API in process: it constructs types by hand, calls them, and asserts on the result. No app boot, no database unless the crate itself owns persistence.

Cargo compiles each top-level tests/*.rs as its own integration test binary. Subdirectories are not compiled as binaries — they hold modules a top-level entry file pulls in with mod. The standard layout:

crates/nest-rs-authn/
├── src/
│ ├── error.rs
│ ├── jwt/
│ │ ├── config.rs
│ │ └── service.rs
│ └── password/
│ └── hash.rs
└── tests/
├── jwt.rs ← one binary, tests/jwt/*.rs modules under it
├── jwt/
│ ├── config.rs
│ └── service.rs
├── password.rs ← another binary
└── common/
└── mod.rs ← shared helpers, not its own binary

Each tests/*.rs is a normal Rust crate root: declare its modules with mod, pull in shared helpers from tests/common/mod.rs.

crates/nest-rs-authn/tests/jwt.rs
mod common;
mod jwt;

Cargo allows both — pick by build cost and harness coherence:

  • One file per concern cluster. A binary per test file means Cargo links the crate once per file; bundling related tests cuts link time. Group tests that share setup (e.g. all JWT tests under tests/jwt.rs).
  • Separate files for independent concerns. A test suite that does not share helpers with another belongs in its own binary, so a rebuild of one does not relink the other.
  • One file is fine for a small crate. tests/<crate-short>.rs with every module declared inside it is a reasonable default until the build cost shows up.

The official Rust idiom for shared test helpers: put them in tests/common/mod.rs, not tests/common.rs. The mod.rs form keeps Cargo from compiling them as their own integration test binary.

crates/nest-rs-authn/tests/common/mod.rs
pub const DEV_PRIVATE_KEY: &str = include_str!("../fixtures/dev.key");
pub const DEV_PUBLIC_KEY: &str = include_str!("../fixtures/dev.pub");
crates/nest-rs-authn/tests/jwt/config.rs
use nest_rs_authn::{JwtConfig, JwtKey, JwtService};
#[test]
fn into_options_selects_eddsa_from_key_pair() {
let options = JwtConfig {
private_key: Some(crate::common::DEV_PRIVATE_KEY.into()),
public_key: Some(crate::common::DEV_PUBLIC_KEY.into()),
..Default::default()
}
.into_options()
.expect("options");
assert!(matches!(options.key, JwtKey::Pem { .. }));
JwtService::new(options).expect("EdDSA service builds");
}

crates/nest-rs-testing/tests/*.rs is the home for harnesses that exercise framework wiring across crates — boot-time access-graph rejection, lifecycle hook ordering, transport contribution. One file per behaviour cluster, same Cargo conventions.

If the crate genuinely owns database access (e.g. nest-rs-seaorm, the SeaORM integration), a real Postgres via EphemeralDatabase is fair game in its integration tests. A test that mocks the database to assert query shape belongs deleted, not rewritten — the queries are exercised end-to-end against a real engine.