Unit vs Integration Tests
Testes unitários exercitam um módulo isoladamente dentro de src/. Testes de integração compilam como crates separadas em tests/ e chamam apenas a API pública, espelhando como usuários downstream dependem da sua crate.
Receita
// src/lib.rs - teste unitário
#[cfg(test)]
mod tests {
#[test]
fn internal_helper() { /* usa fn privada */ }
}// tests/api_smoke.rs - teste de integração
use my_crate::Client;
#[test]
fn creates_client() {
let _ = Client::new("http://localhost");
}Quando usar isso: Testes unitários para lógica; testes de integração para conexões, manipuladores HTTP e limites de banco de dados.
Exemplo de Trabalho
my_service/
├── src/
│ ├── lib.rs
│ ├── domain.rs
│ └── api.rs
└── tests/
├── health.rs
└── orders.rs
// tests/orders.rs
use axum::body::Body;
use axum::http::{Request, StatusCode};
use tower::ServiceExt;
use my_service::app;
#[tokio::test]
async fn post_order_returns_201() {
let app = app();
let response = app
.oneshot(Request::builder().method("POST").uri("/orders").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::CREATED);
}O que isso demonstra:
- Testes de integração são crates externas (
use my_service::...) - Apenas itens
pubsão alcançáveis - Testes de integração assíncronos usam
#[tokio::test] - Um arquivo por área de comportamento mantém falhas localizadas
Mergulho Profundo
Comparação
| Aspecto | Unitário (#[cfg(test)]) | Integração (tests/) |
|---|---|---|
| Localização | Dentro de src/**/*.rs | tests/*.rs |
| Visibilidade | API Privada | Apenas API Pública |
| Unidade de Compilação | Mesma crate | Crate separada por arquivo |
| Velocidade | Mais rápido | Mais lento (vincula a biblioteca completa) |
Múltiplos Arquivos de Integração
Cada tests/*.rs é sua própria crate. Compartilhe helpers via tests/common/mod.rs (não descoberto automaticamente como teste) ou um tests/common.rs incluído com mod common;.
Armadilhas
- Testar funções privadas apenas com testes unitários - lacunas de integração quando a API
pubse desvia. Correção: testes de integração para cada endpoint ou tipo público. - Estado estático mutável compartilhado em testes paralelos - falhas intermitentes. Correção:
#[serial_test::serial]ou isolar o estado por teste. - Configuração pesada duplicada - fixtures copiadas e coladas. Correção: módulo
tests/supportcompartilhado (Organização de Testes). - Testes de integração acessando o banco de dados de produção - falhas perigosas em CI. Correção:
testcontainersou fakes em memória. - Um único arquivo de integração gigante - compilações incrementais lentas. Correção: dividir por área de funcionalidade.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Testes de Documentação | Exemplos em docs precisam compilar | Conexões complexas entre múltiplas crates |
#[cfg(test)] em tests/common | Fixtures compartilhadas | Precisa testar helpers privados |
Apenas mod tests dentro da crate | Crates de algoritmo puro | Testes de limites de API HTTP/CLI |
FAQs
Quantos de cada?
Muitos testes unitários para lógica pura; menos testes de integração cobrindo caminhos críticos do usuário e tratamento de erros.
Testes de integração podem usar dev-dependencies?
Sim. A crate de teste se vincula à sua biblioteca e pode usar dev-deps declaradas no Cargo.toml principal.
Por que tests/common.rs não é executado como um teste?
Apenas tests/*.rs no nível superior são crates de teste. Submódulos são código de suporte.
Como testar uma crate binária?
Extraia a lógica para uma crate de biblioteca; testes de integração importam a biblioteca. Binários permanecem como wrappers main finos.
Testes de integração em Workspaces?
Coloque sob o tests/ do pacote específico ou uma crate membro dedicada integration-tests, dependendo do escopo.
Testes de integração rodam em paralelo?
Sim, por padrão. Use --test-threads=1 quando os testes disputarem portas ou arquivos.
Como filtrar apenas integração?
cargo test --test orders executa um arquivo de integração.
Manipuladores devem ser testados unitariamente?
Extraia a lógica pura para funções com testes unitários; mantenha um teste de integração por rota para códigos de status.
E os testes de integração #[ignore]?
Use para testes que requerem docker; execute com cargo test -- --ignored em CI noturna.
Como isso se relaciona com nextest?
cargo nextest run respeita a mesma divisão unitária/integração com agendamento mais rápido.
Relacionados
- Noções Básicas de Testes - asserções e
cargo test - Organização de Testes - fixtures
- Testando Código Assíncrono -
#[tokio::test] - Melhores Práticas de Teste - orientação de pirâmide
Versões da Pilha: 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+.