Teste da API
Testes de integração
Teste da API é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 4 aulas no total.
Por que testar uma API?
Os testes dão confiança de que os endpoints funcionam corretamente e continuam funcionando conforme você altera o código. Para uma API REST, os testes mais valiosos são os testes de integração: eles exercitam o roteador real de ponta a ponta, enviando requisições e verificando as respostas.
Esta lição aborda testes unitários, o recurso oneshot do Tower e testes de integração completos.
Testes unitários para lógica pura
A lógica que não interage com a rede, como a validação, pode ser testada com testes unitários comuns do Rust. Coloque-os em um módulo #[cfg(test)] ao lado do código. Eles são executados rapidamente com cargo test.
fn validate_title(title: &str) -> bool {
!title.trim().is_empty()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_empty() {
assert!(!validate_title(" "));
assert!(validate_title("buy milk"));
}
}Testes assíncronos
Os manipuladores são assíncronos, portanto as funções de teste que aguardam operações precisam ser executadas em um ambiente de execução. Use #[tokio::test] em vez de #[test]. Ele inicia um ambiente de execução Tokio para esse teste, permitindo chamar código assíncrono com .await.
async fn add(a: i32, b: i32) -> i32 { a + b }
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn adds() {
assert_eq!(add(2, 3).await, 5);
}
}Testando sem uma rede
Os roteadores Axum implementam o trait Service do Tower, portanto você pode fornecer requisições diretamente a eles sem vincular uma porta. O método oneshot recebe uma única Request e retorna a Response. Isso torna os testes de integração rápidos e determinísticos.
// dev-dependencies needed: tower (for ServiceExt), http-body-util
use axum::{Router, routing::get};
use axum::http::{Request, StatusCode};
use axum::body::Body;
use tower::ServiceExt; // brings in oneshot
async fn check() {
let app = Router::new().route("/health", get(|| async { "ok" }));
let res = app
.oneshot(Request::builder().uri("/health").body(Body::empty()).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}Verificando o código de status
A primeira coisa que a maioria dos testes verifica é o status HTTP. Um recurso ausente deve retornar 404, uma criação bem-sucedida deve retornar 201 e um corpo inválido deve retornar 400. Construa uma requisição para a rota e compare res.status() com o código esperado.
use axum::http::{Request, StatusCode};
use axum::body::Body;
use tower::ServiceExt;
async fn missing_returns_404(app: axum::Router) {
let res = app
.oneshot(Request::builder()
.uri("/todos/999")
.body(Body::empty()).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::NOT_FOUND);
}Lendo o corpo da resposta
Para verificar o JSON, reúna o corpo da resposta em bytes e desserialize-o. O auxiliar http_body_util::BodyExt::collect reúne o corpo; em seguida, serde_json o analisa em seu modelo para verificações no nível dos campos.
use http_body_util::BodyExt;
async fn read_json(res: axum::http::Response<axum::body::Body>) {
let bytes = res.into_body().collect().await.unwrap().to_bytes();
let todo: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(todo["done"], false);
}Enviando um corpo JSON
Para testar um POST, construa uma requisição com um corpo JSON e o cabeçalho de tipo de conteúdo correto. Serialize sua estrutura de entrada, defina content-type: application/json e passe-a por oneshot.
use axum::http::{Request, StatusCode, header};
use axum::body::Body;
use tower::ServiceExt;
async fn create_returns_201(app: axum::Router) {
let body = serde_json::json!({ "title": "test" }).to_string();
let res = app
.oneshot(Request::builder()
.method("POST").uri("/todos")
.header(header::CONTENT_TYPE, "application/json")
.body(Body::from(body)).unwrap())
.await.unwrap();
assert_eq!(res.status(), StatusCode::CREATED);
}Um construtor reutilizável de aplicações de teste
Cada teste deve começar com um estado limpo. Escreva um auxiliar que construa um roteador novo com um banco de dados novo na memória ou de teste. Chame-o em cada teste para isolá-los, impedindo que um teste afete outro.
use axum::Router;
use std::sync::{Arc, Mutex};
fn test_app() -> Router {
let store = Arc::new(Mutex::new(Vec::new()));
build(store) // same build() the real server uses
}O diretório de testes
Os testes de integração ficam em uma pasta tests/ no nível superior. Cada arquivo ali é compilado como um crate separado que usa a API pública da sua biblioteca. Isso obriga você a testar pela mesma interface que os usuários reais veem.
tests/api.rscontém os testes dos endpoints.- Execute todos com
cargo test.
// tests/api.rs
use my_api::build_router; // exported from lib.rs
#[tokio::test]
async fn health_ok() {
let _app = build_router();
// send a request and assert ...
}Testando com um banco de dados
Quando os manipuladores usam sqlx, os testes também precisam de um banco de dados. Estratégias comuns: um banco de dados de teste dedicado, transações revertidas após cada teste ou a macro #[sqlx::test] do sqlx, que provisiona automaticamente um banco de dados limpo para cada teste.
// requires sqlx test features and a DATABASE_URL
use sqlx::PgPool;
#[sqlx::test]
async fn inserts_todo(pool: PgPool) {
let todo = create(&pool, "learn rust").await.unwrap();
assert_eq!(todo.title, "learn rust");
assert_eq!(todo.done, false);
}O que testar
Busque uma suíte equilibrada:
- Caminho feliz: requisições válidas retornam o status e o corpo corretos.
- Erros: recursos ausentes retornam 404; entradas inválidas retornam 400.
- Casos-limite: listas vazias, valores de limite e duplicatas.
Execute cargo test na CI para detectar regressões antes da implantação.
Verificação rápida
Teste sua compreensão dos testes da API.
Recapitulação
Você aprendeu a testar uma API REST em Rust:
- Faça testes unitários da lógica pura; use
#[tokio::test]para código assíncrono. oneshotexecuta o roteador diretamente, sem precisar de uma porta.- Construa requisições com corpos e cabeçalhos; reúna e analise os corpos das respostas.
- Use um construtor de aplicações novo em cada teste para garantir o isolamento; coloque os testes de integração em
tests/. #[sqlx::test]provisiona bancos de dados limpos; cubra caminhos felizes, erros e casos-limite.
Perguntas Frequentes
A aula “Teste da API” é grátis?
Sim — o texto completo de “Teste da API” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 4 aulas no total.
O que vou aprender em “Teste da API”?
Testes de integração Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Learn Rust Coding?
Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Teste da API”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Learn Rust Coding?
Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.