Como Testar a API Rust?

INEZA Felin-Michel

INEZA Felin-Michel

13 maio 2026

Como Testar a API Rust?

Apidog para empresas

Implantação local

SSO & RBAC

Conforme SOC 2

Explorar Apidog Enterprise

Rust oferece um servidor HTTP rápido e com segurança de tipo em algumas centenas de linhas. O que ele não oferece é um ciclo de feedback rápido para testar esse servidor. O ciclo de compilação é longo, cargo test executa tudo novamente em uma única alteração de trait, e a maioria dos frameworks HTTP de Rust faz você escrever um teste de integração separado para cada endpoint antes mesmo de você tê-lo chamado uma vez. Se você deseja entregar uma API e não apenas um binário, você precisa de uma ferramenta que viva fora do conjunto de ferramentas do Rust e se comunique com o servidor em execução.

Este guia detalha o fluxo de trabalho completo de teste de API Rust dentro do Apidog: apontando o Apidog para o seu servidor Axum ou Actix, construindo requisições para seus endpoints, validando JSON serializado pelo Serde, lidando com autenticação JWT, simulando endpoints para que o frontend possa avançar enquanto você finaliza o handler, e empacotando tudo como um cenário de teste de CI. Ao final, você terá um projeto Apidog reutilizável que detecta desvios de contrato antes que cargo build --release seja concluído.

Se você vem de um fluxo de trabalho Postman ou curl, você também obtém gratuitamente os recursos de design-first do Apidog: uma especificação OpenAPI gerada a partir de suas requisições salvas, URLs de mock compartilháveis e ambientes de equipe. Ignore a história de migração do Postman para uma leitura separada; esta postagem permanece focada em Rust.

TL;DR

Por que testar uma API Rust fora do conjunto de ferramentas Rust

cargo test é bom. É também lento, opaco para colegas de equipe que não usam Rust e construído em torno de código em vez de HTTP. Se você deseja verificar se seu handler retorna o código de status correto, o formato JSON correto, os cabeçalhos corretos e a mensagem de erro correta quando a entrada está malformada, você escreve uma nova chamada tower::ServiceExt::oneshot para cada caso. Então você mantém esse teste conforme o handler muda. Então você o escreve novamente em JavaScript para que o frontend possa atingir um mock.

Apidog oferece uma única camada de contrato sobre o servidor em execução. A requisição existe uma vez. As asserções ficam ao lado da requisição. Colegas de equipe de frontend abrem o mesmo projeto e veem as mesmas requisições que você. Quando Serde receber um atributo #[serde(rename_all = "camelCase")] daqui a três semanas, o teste que falhará será o do Apidog, não o que vai para produção.

Três razões concretas para adicionar o Apidog a um fluxo de trabalho Rust:

  1. Verificações de contrato se desvinculam da compilação. O Apidog é executado contra um binário em execução. Você para de esperar o rustc para validar que seu endpoint ainda retorna 200.
  2. Mocks são compartilháveis. Um desenvolvedor de frontend em outro fuso horário obtém uma URL que retorna o JSON correto, não uma mensagem no Slack dizendo “o handler ainda não está pronto.”
  3. OpenAPI gratuitamente. O Apidog pode gerar um documento OpenAPI 3.1 a partir das requisições salvas. Você entrega isso a qualquer pessoa que queira um cliente tipado sem precisar escrever uma anotação utoipa ou aide em cada rota.

Passo 1: Adicione seu servidor Rust como um ambiente Apidog

Inicie sua API Rust. Para um projeto Axum, o boilerplate é:

use axum::{routing::get, Router};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/healthz", get(|| async { "ok" }));
    let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Abra o Apidog, crie um novo projeto, então abra o Gerenciamento de Ambiente (dropdown superior direito) e adicione um ambiente chamado Rust Local:

Variável Valor
baseUrl http://localhost:3000
token deixe vazio por enquanto
apiVersion v1

Adicione um segundo ambiente chamado Rust Staging com a URL base implantada. O Apidog define o escopo das variáveis por ambiente, então você alterna de local para staging com um clique no dropdown. Sem precisar usar 'localizar e substituir' em requisições salvas.

Passo 2: Acesse o primeiro endpoint

Crie uma pasta chamada Rust API dentro do projeto, então uma nova requisição:

Clique em Enviar. Se seu servidor estiver em execução, você receberá um 200 com corpo ok. Salve isso como health-check. É o teste de fumaça mais simples possível e confirma que o ambiente e a URL base funcionam antes que você escreva algo mais interessante.

Se você receber um erro de conexão recusada, seu servidor não está vinculado a 0.0.0.0 ou a porta está errada. O TcpListener::bind("127.0.0.1:3000") padrão do Rust rejeitará requisições vindas de qualquer coisa que resolva para localhost em uma interface diferente; vincule a 0.0.0.0 para desenvolvimento local para que o Apidog e os contêineres Docker possam alcançá-lo.

Passo 3: Teste requisições e respostas JSON com Serde

A forma mais comum de API Rust é um handler de JSON de entrada e saída, apoiado por uma struct Serde. Adicione uma rota POST /users:

use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

async fn create_user(Json(payload): Json<CreateUser>) -> Json<User> {
    Json(User { id: 1, name: payload.name, email: payload.email })
}

let app = Router::new().route("/users", post(create_user));

No Apidog, crie uma requisição:

{
  "name": "Ada Lovelace",
  "email": "ada@example.com"
}

Envie. Você receberá o JSON User. Salve como create-user.

Agora abra a aba Testes e adicione asserções:

pm.test("Status is 200", () => {
  pm.expect(pm.response.code).to.eql(200);
});

pm.test("Body has id, name, email", () => {
  const body = pm.response.json();
  pm.expect(body).to.have.property("id");
  pm.expect(body.name).to.eql("Ada Lovelace");
  pm.expect(body.email).to.match(/^[^@]+@[^@]+$/);
});

Na próxima vez que alguém adicionar #[serde(rename_all = "camelCase")] à struct e o formato da sua resposta mudar de user_id para userId, este teste falhará antes que a alteração seja implantada. Esse é o contrato que o Apidog oferece e que cargo test não oferece, porque cargo test executa seu código Rust contra seus tipos Rust e passaria feliz com qualquer um dos formatos.

Passo 4: Cobrir casos de rejeição do Serde

A parte interessante do tratamento de JSON em Rust é o que o Serde faz com entradas inválidas. Por padrão, o Axum retorna um 422 Unprocessable Entity sem detalhes. Construa três requisições que intencionalmente quebram o esquema:

Requisição Corpo Esperado
create-user-missing-email { "name": "Ada" } 422, corpo menciona missing field email
create-user-extra-field { "name": "Ada", "email": "a@b.c", "admin": true } 200 se #[serde(deny_unknown_fields)] estiver ausente; 422 caso contrário
create-user-wrong-type { "name": 1, "email": "a@b.c" } 422, menciona invalid type: integer

Afirme cada código de status em Testes. Esta é a maneira mais barata de documentar sua política de validação real. Se você ativar deny_unknown_fields mais tarde, o segundo teste ficará vermelho e informará que o contrato público mudou.

Passo 5: Teste rotas protegidas por JWT

A maioria das APIs Rust em produção esconde handlers atrás de um middleware de autenticação. O extrator JWT de axum-extra do Axum é o padrão comum:

use axum_extra::extract::cookie::PrivateCookieJar;
use jsonwebtoken::{decode, DecodingKey, Validation};

async fn me(jar: PrivateCookieJar) -> Result<Json<User>, StatusCode> {
    let token = jar.get("token").ok_or(StatusCode::UNAUTHORIZED)?;
    let claims = decode::<Claims>(token.value(), &DecodingKey::from_secret(b"secret"), &Validation::default())
        .map_err(|_| StatusCode::UNAUTHORIZED)?;
    Ok(Json(User { id: claims.claims.sub, name: "Ada".into(), email: "ada@example.com".into() }))
}

No Apidog, você não precisa criar um JWT manualmente a cada execução de teste. Crie um Script de Pré-Requisição na pasta:

const jwt = require("jsonwebtoken");
const token = jwt.sign(
  { sub: 1, exp: Math.floor(Date.now() / 1000) + 3600 },
  "secret"
);
pm.environment.set("token", token);

Abra as configurações da pasta, defina Autenticação como Bearer Token, valor {{token}}. Cada requisição na pasta agora assina e apresenta um JWT novo. Erros de token expirado desaparecem de suas execuções de teste. Para um aprofundamento sobre o lado da asserção, veja como testar autenticação JWT em APIs.

Passo 6: Teste de streaming e Server-Sent Events

Frameworks web em Rust têm streaming de primeira classe. A resposta Sse do Axum envolve um futures::Stream e emite chunks text/event-stream. O formato de comunicação é data: { ... }\n\n por quadro, terminado pelo fechamento da conexão ou por um evento done explícito.

Uma requisição que consome isso se parece com qualquer GET, mas o painel de resposta no Apidog muda para o modo de streaming quando o Content-Type é text/event-stream. Você vê cada quadro conforme ele chega, com carimbos de data/hora. Essa é a visualização que você precisa ao depurar um problema de backpressure ou um flush ausente.

O que afirmar:

Se seu endpoint usa WebSockets em vez de SSE, o Apidog possui um tipo de requisição WebSocket separado. O padrão é o mesmo: construa a conexão uma vez, salve a sequência de mensagens, afirme as respostas.

Passo 7: Simule a API Rust para desenvolvimento frontend paralelo

O frontend raramente é bloqueado pelos tempos de compilação do Rust. Ele é bloqueado por handlers que ainda não existem. Os mocks do Apidog permitem que você publique uma URL estável que retorna o contrato que você e o frontend concordaram, antes que o handler seja lançado.

Clique com o botão direito em create-user, escolha Smart Mock e habilite-o. O Apidog agora serve uma resposta User sintética em https://mock.apidog.com/m1/<projectId>/users. O corpo do mock corresponde ao seu exemplo salvo. A URL do mock aceita o mesmo formato de corpo, então o frontend pode fazer um POST contra ela como se fosse o servidor Rust real.

Para mocks dinâmicos, mude para Advanced Mock e escreva um script:

return {
  id: Math.floor(Math.random() * 10000),
  name: body.name,
  email: body.email,
  createdAt: new Date().toISOString()
};

Esse mock responde ao que o frontend enviar, com um id e timestamp gerados. Quando o handler Rust estiver pronto, o frontend retorna sua URL base para http://localhost:3000 e nada mais muda. Para mais informações sobre este padrão, a equipe também aborda a construção e teste de uma API Spring Boot e o fluxo de trabalho geral de teste de API; mesma ideia, diferentes runtimes.

Passo 8: Salve como um cenário de teste de CI

Cenários de Teste do Apidog encadeiam requisições com variáveis compartilhadas e as executam sem interface gráfica. Construa um cenário:

  1. health-check, afirme 200.
  2. create-user, afirme 200, capture body.id em uma variável.
  3. create-user-missing-email, afirme 422.
  4. me (com a pré-requisição JWT), afirme 200 e que o id retornado corresponde ao id capturado.
  5. Requisição SSE, afirme que o stream é concluído em 5 segundos.

Exporte o cenário como JSON, commite-o para seu repositório em tests/apidog/ e chame-o do CI:

- name: Run API contract tests
  run: |
    cargo build --release
    ./target/release/myserver &
    sleep 2
    apidog-cli run tests/apidog/contract.json --env "Rust Local"

Cada PR que afeta um handler agora é executado contra um binário Rust ativo com o conjunto completo de contratos. Se uma renomeação Serde, uma mudança de código de status ou um ajuste na validação JWT quebrar o formato público, o CI o detecta antes que o botão de mesclagem fique verde.

Passo 9: Gere OpenAPI a partir das requisições salvas

Quando o conjunto de requisições estiver estável, abra o menu Exportar do Apidog e escolha OpenAPI 3.1. Você obtém um documento de especificação cobrindo cada requisição salva, com os corpos que você enviou como exemplos. Entregue isso a qualquer pessoa que esteja gerando um cliente tipado (TypeScript, Swift, Kotlin, Python) e eles receberão um contrato que corresponde ao que seu servidor Rust retorna hoje, não ao que alguém escreveu manualmente em um .yaml seis meses atrás.

Se você deseja que a especificação seja adicionada ao seu repositório Rust, execute apidog-cli export do CI e grave-a em openapi.json. O próximo cargo build não muda, mas cada consumidor de sua API obtém a verdade no disco.

FAQ

Conclusão

APIs Rust merecem um ciclo de feedback que não dependa do compilador. Uma coleção de requisições no Apidog oferece esse ciclo: HTTP real, asserções reais, mocks reais para o frontend e um cenário de CI que é executado contra o binário ativo. Crie as requisições acima uma vez, e cada futura alteração no seu handler Axum ou Actix se tornará uma execução de teste controlada em vez de uma surpresa em tempo de execução.

Baixe o Apidog e aponte-o para o seu servidor Rust. A configuração leva menos de dez minutos. O resultado é um contrato que você controla, desacoplado do cargo, e uma equipe de frontend que para de perguntar quando o handler estará pronto.

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs