Test Organization
Organized tests scale with your codebase: colocate unit tests, share integration fixtures, and keep setup explicit so parallel cargo test stays reliable.
Busca en todas las páginas de la documentación
Organized tests scale with your codebase: colocate unit tests, share integration fixtures, and keep setup explicit so parallel cargo test stays reliable.
// tests/common/mod.rs
pub fn test_app() -> axum::Router {
my_api::app_with_config(test_config())
}// tests/orders.rs
mod common;
#[tokio::test]
async fn creates_order() {
let app = common::test_app();
// ...
}When to reach for this: More than a handful of integration tests or repeated DB/API setup.
tests/
├── common/
│ ├── mod.rs
│ └── db.rs
├── orders.rs
└── users.rs
// tests/common/mod.rs
pub mod db;
use sqlx::PgPool;
pub async fn pool() -> PgPool {
db::migrate_and_connect().await
}// tests/orders.rs
mod common;
#[sqlx::test]
async fn inserts_order(pool: sqlx::PgPool) {
let id = my_api::create_order(&pool, "sku-1").await.unwrap();
assert!(id > 0);
}What this demonstrates:
tests/common/mod.rs is not a test crate by itselfmod common; shares helpers across integration files#[sqlx::test] provides per-test DB setup| Pattern | Use |
|---|---|
Inline mod tests | Small modules |
src/foo/tests.rs | Large foo.rs |
#[path = "tests/unit/foo_tests.rs"] | Rare split |
pub struct OrderBuilder {
sku: String,
}
impl OrderBuilder {
pub fn new() -> Self {
Self { sku: "default".into() }
}
pub fn sku(mut self, s: &str) -> Self {
self.sku = s.into();
self
}
pub fn build(self) -> Order {
Order { sku: self.sku }
}
}OrderBuilder::new().sku("x").build()OnceLock pool - tests pollute each other. Fix: transactions rolled back per test or #[serial_test::serial].Result from helpers; use ? in tests.test_app() helper with shared middleware stack.tests/ - cannot access private API. Fix: move logic test to src or expose #[cfg(test)] helpers.| Alternative | Use When | Don't Use When |
|---|---|---|
test-log crate | Capture logs in tests | Simple pure functions |
rstest parameterized tests | Many input cases | One-off assertions |
Separate integration-tests crate | Workspace-wide E2E | Single package services |
tests/fixtures/ or tests/data/; load with include_str! or std::fs from CARGO_MANIFEST_DIR.
Use #[tokio::test] per test; avoid custom global runtime unless benchmarking.
Within tests/common, pub for cross-file use; not exported to production code.
Dedicated workspace member e2e-tests depending on API + CLI crates.
#[cfg(feature = "postgres")] on modules or cargo test --features postgres.
mod tests for unit; integration files named by behavior orders.rs, not test_orders.rs.
Yes in tests/common/mock.rs; see Mocking & Test Doubles.
Truncate in #[sqlx::test] fixture or wrap each test in a transaction and rollback.
Only with isolated resources (unique ports, containers per test, or mutex around shared resource).
CONTRIBUTING.md section: unit vs integration, how to run subsets, env vars for DB URL.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 16 jul 2026