Quando fiz o curso CS50 de Introdução à Ciência da Computação do MIT, uma das lições que mais me marcou foi o uso de algoritmos clássicos de busca, como Depth-First Search e Breadth-First Search, para resolver labirintos. O programa percorria corredores, marcava bifurcações, identificava becos sem saída e, ao final, apresentava um mapa completo do ambiente.

Implementando Testes Combinatórios em APIs: Do Algoritmo de Labirinto à Cobertura Total

Anos depois, ao encarar uma base de código com dezenas de planos de assinatura, limites variáveis e mais de uma centena de rotas, percebi que estava diante de um labirinto, só que de regras de negócio. Foi assim que comecei a aplicar a mesma lógica de exploração exaustiva em testes automatizados. O resultado foi uma pequena suíte que não se limita a verificar cenários felizes: ela mapeia todas as combinações possíveis de estado do sistema e só interrompe a execução quando encontra anomalias.

Neste artigo vou mostrar como você pode levar essa ideia para qualquer stack, sem depender de ferramentas especializadas. Não vou prometer que isso é uma revolução, property-based testing, fuzzing e model-based testing já existem há décadas , , mas a aplicação prática desse “explorador de labirinto” em testes de integração de APIs tem se mostrado incrivelmente eficaz e de baixa manutenção.

Testes Cenário-A-Cenário

Testes tradicionais costumam ser escritos assim:

```python
def test_usuario_gratuito_nao_pode_criar_mais_de_2_recursos():

Arrange

usuario = cria_usuario_gratuito()

Act

cria_recurso(usuario)

1º recurso → OK

cria_recurso(usuario)

2º recurso → OK

Assert

with pytest.raises(PermissionError):
cria_recurso(usuario)

3º recurso → bloqueado

```

Esse teste é importante, mas cobre uma única combinação de estado. Em produção, aquele mesmo usuário pode ter um plano expirado, um trial ativo, um histórico de upgrades e downgrades, ou uma mistura de recursos legados com recursos novos. A explosão combinatória é enorme: se você tem 10 estados de conta e 15 configurações de carga, já são 150 cenários. Nenhum time escreve 150 testes manuais para uma única regra, e aí moram os bugs silenciosos que só aparecem naquele cruzamento específico que ninguém testou.

A metáfora do labirinto ajuda a enxergar o problema: cada estado da conta é uma bifurcação, cada configuração de recursos é uma sala diferente, e cada rota da API é um corredor. O teste tradicional manda o rato entrar e virar à direita uma vez. Nosso objetivo é fazer o rato percorrer todos os corredores e nos contar onde encontrou portas trancadas onde não deviam, ou portas abertas onde deveriam haver bloqueio.

Explorador Combinatório de Limites e Estados

A ideia central é cruzar uma lista de estados de conta com uma lista de cargas de recursos e, para cada combinação, verificar se as funções de cálculo de limite do sistema permanecem consistentes.

Em vez de testar “usuário free tem limite 2”, a matriz gera:

  • Estados: free, trial ativo, pago mensal, pago anual, expirado, cancelado, sem nenhum registro no banco, com múltiplas assinaturas, etc.
  • Cargas: zero recursos, exatamente no limite, um acima do limite, carga industrial (centenas de recursos), mix de tipos de recursos diferentes.

Multiplicando, você obtém algo como 13 × 16 = 208 cenários. O laço de exploração executa cada combinação em um banco de dados isolado (um clone de produção ou um banco de testes), popula os recursos, invoca as funções que calculam o limite (tanto pela API quanto pelo frontend/backoffice, se houver lógica duplicada) e compara os resultados.

O teste só falha se encontrar divergências. Em uma das execuções reais, descobrimos que a API contava dispositivos de um tipo, mas o Dashboard ignorava um tipo novo de recurso, uma divergência que existia há semanas e que nenhum teste manual tinha pego, porque o cenário específico (trial expirado + mix de três tipos de recurso) nunca era testado.

Simulador de Ciclo de Vida

Aqui o foco é o comportamento ao longo do tempo. O teste simula um cliente real percorrendo todo o ciclo de vida:

  • cadastro → uso gratuito até o limite → bloqueio
  • ativação de trial → expiração do trial → bloqueio de re‑trial (anti-abuso)
  • upgrade para plano pago → adição de expansões → cancelamento → retorno ao limite gratuito

A cada transição, o teste recalcula o que o sistema deve fazer e verifica se a implementação está de acordo. Isso cobre comportamentos complexos e permite identificar problemas que só aparecem em cenários específicos.

Conclusão

Adapte a lógica de exploração exaustiva de labirintos para testes automatizados, cobrindo todas as combinações possíveis de estado do sistema. Não dependa de ferramentas especializadas. A aplicação prática desse

AlgoritmoApisImplementandoLabirintoTestes