Organização de Testes
Testes organizados escalam com sua base de código: coloque testes unitários próximos ao código, compartilhe fixtures de integração e mantenha a configuração explícita para que cargo test paralelo permaneça confiável.
Receita
// 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();
// ...
}Quando usar isso: Mais do que um punhado de testes de integração ou configuração repetida de banco de dados/API.
Exemplo de Trabalho
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);
}O que isso demonstra:
tests/common/mod.rsnão é um crate de teste por si sómod common;compartilha helpers entre arquivos de integração#[sqlx::test]fornece configuração de banco de dados por teste- Fixtures assíncronas permanecem em helpers assíncronos
Análise Profunda
Layout de Testes Unitários
| Padrão | Uso |
|---|---|
mod tests inline | Módulos pequenos |
src/foo/tests.rs | foo.rs grande |
#[path = "tests/unit/foo_tests.rs"] | Divisão rara |
Fixtures de Builder
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 }
}
}- Builders reduzem a duplicação de configuração de teste
- Objeto válido padrão; substitua um campo por teste
- Mantém os testes legíveis:
OrderBuilder::new().sku("x").build()
Armadilhas
- Pool estático global
OnceLock- testes poluem uns aos outros. Correção: transações revertidas por teste ou#[serial_test::serial]. - tests/common.rs E tests/common/mod.rs - resolução de módulo confusa. Correção: escolha um layout e documente no README.
- Pânicos de Fixture - obscurecem qual teste falhou na configuração. Correção: retorne
Resultde helpers; use?em testes. - Configuração de cliente HTTP copiada e colada - desvio entre arquivos. Correção: helper
test_app()com pilha de middleware compartilhada. - Testes unitários em
tests/- não podem acessar API privada. Correção: mova o teste de lógica parasrcou exponha helpers#[cfg(test)].
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
crate test-log | Capturar logs em testes | Funções puras simples |
testes parametrizados rstest | Muitos casos de entrada | Asserções únicas |
Crate separado integration-tests | E2E em todo o workspace | Serviços de pacote único |
FAQs
Onde colocar arquivos de dados de teste?
tests/fixtures/ ou tests/data/; carregue com include_str! ou std::fs de CARGO_MANIFEST_DIR.
Como compartilhar o runtime Tokio?
Use #[tokio::test] por teste; evite runtime global personalizado, a menos que esteja fazendo benchmarking.
Helpers devem ser pub?
Dentro de tests/common, pub para uso entre arquivos; não exportado para código de produção.
Como organizar E2E de workspace?
Membro de workspace dedicado e2e-tests dependendo dos crates API + CLI.
E sobre testes com feature-gated?
#[cfg(feature = "postgres")] em módulos ou cargo test --features postgres.
Como nomear módulos de teste?
mod tests para unitário; arquivos de integração nomeados por comportamento orders.rs, não test_orders.rs.
Posso usar traits para duplos de teste?
Sim em tests/common/mock.rs; veja Mocking & Test Doubles.
Como limpar bancos de dados?
Truncar em fixture #[sqlx::test] ou envolver cada teste em uma transação e reverter.
Testes de integração paralelos são seguros?
Somente com recursos isolados (portas únicas, contêineres por teste ou mutex em torno de recurso compartilhado).
Documentar layout de teste?
Seção CONTRIBUTING.md: unitário vs integração, como executar subconjuntos, variáveis de ambiente para URL do banco de dados.
Relacionados
- Testes Unitários vs. Integração - regras de posicionamento
- Mocking & Test Doubles - costuras de traits
- Testando Código Assíncrono - configuração do Tokio
- Cobertura & CI - layout do pipeline
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.