DEV Community

Denis Augusto
Denis Augusto

Posted on

Seu teste bate na API de verdade? Um dia ela vai te trair

O teste quebrou. E não foi você.

Segunda de manhã, você abre o projeto e o CI tá vermelho.

Você não mexeu em nada desde sexta. Abre o log: o teste de integração com a API de pagamento falhou. Timeout.

O gateway deles teve uma instabilidade às 3 da manhã. Seu código tá perfeito. Mesmo assim, o build tá vermelho, o deploy travado, e agora você perde meia hora provando que o problema não é seu.

Já passou por isso? Teste que depende de API externa é assim: lento, instável e quebra por motivos que não são culpa sua. E tem o cenário pior — o teste que roda de verdade contra a API de pagamento, e a cada php artisan test você dispara 30 cobranças reais no sandbox até o gateway achar suspeito e bloquear.

O problema: seu teste não deveria conhecer a internet

Olha esse teste. Parece razoável, né?

public function test_busca_o_cep(): void
{
    // isso bate no ViaCEP de VERDADE, toda vez que o teste roda
    $endereco = $this->service->buscarCep('01001000');

    $this->assertEquals('São Paulo', $endereco['cidade']);
}
Enter fullscreen mode Exit fullscreen mode

O que tem de errado? Tudo que importa num teste:

  • Lento — cada teste espera a ida e volta pela rede.
  • Instável — a API cair, ficar lenta ou mudar o retorno quebra seu teste sem seu código ter mudado.
  • Perigoso — se for uma API que cobra, envia e-mail ou cria pedido, você tá fazendo isso pra valer a cada execução.
  • Sem controle — como você testa "o que acontece quando a API devolve erro 500"? Não dá pra pedir pra ela cair na hora certa.

O teste devia verificar o seu código. Não a saúde do servidor dos outros.

A solução: Http::fake() intercepta tudo

Aqui vem o $pedido->restore() dessa história. Se você usa o HTTP Client do Laravel (Http::get(), Http::post()...), uma linha resolve:

use Illuminate\Support\Facades\Http;

public function test_busca_o_cep(): void
{
    // A partir daqui, NENHUMA requisição sai de verdade.
    // Você diz o que cada URL deve responder.
    Http::fake([
        'viacep.com.br/*' => Http::response([
            'localidade' => 'São Paulo',
            'uf'         => 'SP',
        ], 200),
    ]);

    $endereco = $this->service->buscarCep('01001000');

    $this->assertEquals('São Paulo', $endereco['cidade']);
}
Enter fullscreen mode Exit fullscreen mode

Pronto. O Http::fake() intercepta a requisição e devolve a resposta que você mandou, sem tocar na internet. O teste roda offline, em milissegundos, e dá o mesmo resultado toda vez. Você virou dono do comportamento da API.

Como usar na prática

O fake() é flexível. Alguns cenários que você vai usar toda hora:

// Fingir QUALQUER requisição como 200 vazio (o mais simples)
Http::fake();

// Respostas diferentes por URL
Http::fake([
    'github.com/*'  => Http::response(['plano' => 'pro'], 200),
    'viacep.com.br/*' => Http::response('', 404), // simula CEP não achado
]);

// Simular a API fora do ar (erro 500) — o caminho que ninguém testa
Http::fake([
    'gateway.com/*' => Http::response('Erro interno', 500),
]);
Enter fullscreen mode Exit fullscreen mode

Repara no último: agora você consegue testar o caminho de falha. "Se o gateway devolver 500, meu app mostra uma mensagem amigável ou explode na cara do usuário?" Sem fake(), você nunca teve como responder isso.

Não basta simular: confirme que você mandou certo

Aqui mora a pegadinha que separa o teste que testa do teste que só finge.

Simular a resposta não garante que você montou a requisição direito. Passou o header de autenticação? Mandou o valor no campo certo? O Http::assertSent() confere isso:

Http::fake();

$this->service->cobrar(valor: 150, cartao: 'tok_abc');

Http::assertSent(function (Request $request) {
    return $request->url() === 'https://gateway.com/charges'
        && $request->hasHeader('Authorization')
        && $request['amount'] === 150;
});
Enter fullscreen mode Exit fullscreen mode

Agora o teste garante os dois lados: você mandou o que devia e reagiu certo à resposta.

Bônus: proíba requisições reais escaparem

Tem um detalhe traiçoeiro. Se você esquecer de fakear uma URL, a requisição sai de verdade — e você nem percebe que aquele teste ainda depende da internet.

O preventStrayRequests() fecha essa porta:

Http::preventStrayRequests();

Http::fake([
    'gateway.com/*' => Http::response('ok'),
]);

Http::get('https://gateway.com/charges'); // ok, tá fakeado
Http::get('https://outra-api.com');        // 💥 lança exceção na hora
Enter fullscreen mode Exit fullscreen mode

Qualquer requisição sem fake correspondente estoura na hora, em vez de vazar silenciosamente pra rede. Coloca isso no setUp() da sua suíte de testes e durma tranquilo.

Antes de você fechar a aba

A regra de ouro: teste não conversa com o mundo lá fora. Ele verifica o seu código, de forma rápida e previsível. A conversa real com a API você deixa pra um teste de integração separado, que roda de vez em quando — não a cada commit.

Um detalhe importante: isso só funciona se você usa o HTTP Client do Laravel (Http::). Se ainda chama Guzzle ou cURL na mão, o fake() não te alcança — e migrar pro Http:: é justamente o próximo passo (spoiler: ele também te dá timeout e retry de graça, mas isso é papo pra outro post).

Me conta: você mocka suas APIs nos testes ou ainda reza pro sandbox ficar de pé? 👀

Top comments (0)