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, no network.

A test binary is always the directory form tests/<suite>/main.rs, and <suite> is one of exactly two canonical names:

  • integration — in-process tests of the public API (this page);
  • e2e — tests that need live infrastructure, gated by the nextest binary(e2e) filter (see End-to-end tests).

Flat tests/<x>.rs files don’t exist in this codebase: Cargo compiles each one as its own binary, so a suite scattered across sibling files escapes the binary(e2e) gate and relinks once per file. The directory form answers the layout question once, for every crate:

crates/nest-rs-authn/
├── src/
│ ├── jwt/…
│ └── passport/…
└── tests/
└── integration/ ← one binary named `integration`
├── main.rs ← mod common; mod jwt; mod passport;
├── common/
│ └── mod.rs ← shared helpers
├── jwt/
│ ├── mod.rs ← mod config; mod service;
│ ├── config.rs ← tests src/jwt/config.rs
│ └── service.rs ← tests src/jwt/service.rs
└── passport/…

tests/integration/main.rs is the crate root of the integration binary; mod jwt; resolves to tests/integration/jwt/mod.rs, and a single-file module needs no mod.rs (mod smoke;tests/integration/smoke.rs). Nothing under tests/integration/ is auto-discovered as a second binary.

Inside the suite, the module tree mirrors src/ — the test for src/jwt/service.rs lives at tests/integration/jwt/service.rs. main.rs stays thin, like a mod.rs: a //! header plus mod lines.

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

A tiny suite keeps its tests directly in main.rs and grows submodules as the crate does — the path never changes.

Helpers live inside the suite directory, next to main.rs — as a plain file or a folder, since nothing under tests/integration/ is compiled as a binary. nest-rs-authn uses the folder form because its helpers outgrew one screen:

crates/nest-rs-authn/tests/integration/common/mod.rs (abridged)
/// Ed25519 key pair used across nestrs dev and e2e apps.
pub const DEV_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----\n\n";
pub const DEV_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----\n\n";
pub fn request(headers: &[(&str, &str)]) -> Request { /* … */ }
crates/nest-rs-authn/tests/integration/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/integration/ is the home for harnesses that exercise framework wiring across crates — boot-time access-graph rejection, lifecycle hook ordering, transport contribution. Same layout; one module per behaviour cluster instead of a src/ mirror, since the crate under test is the framework itself.

If the crate genuinely owns a live backend (e.g. nest-rs-seaorm against Postgres, nest-rs-storage against an S3 server), those tests move to the crate’s tests/e2e/main.rs suite — same directory form, gated out of nestrs run test unit by the binary(e2e) filter, never by #[ignore]. 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.

  • End-to-end tests — boot the real AppModule against live Postgres.
  • Unit tests — pure logic next to the code, no DI boot.
  • Testing — the three categories and the test recipe group.

Built by YV17labs