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.
One layout, two suite names
Section titled “One layout, two suite names”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 nextestbinary(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.
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.
Shared helpers
Section titled “Shared helpers”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:
/// 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 { /* … */ }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");}Cross-crate framework wiring
Section titled “Cross-crate framework wiring”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.
When a crate needs live infrastructure
Section titled “When a crate needs live infrastructure”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.
Going further
Section titled “Going further”- 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
testrecipe group.
Built by YV17labs