Pular para conteúdo

0013 — Testes de integração sem SQL cru, sobre a fixture da casa

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-08-24 · Decidido em: 2026-08-24

Contexto

Os testes @MicronautTest provisionavam o Postgres pelo plugin io.micronaut.test-resources (divergindo do padrão da casa, ADR 0019 / xadm-comum-teste, que a extração house-wide fixou como PostgresTestResource) e misturavam escrita por repositório @Transactional com conexões JDBC cruas no corpo do teste. Sob a tx-de-teste padrão do @MicronautTest (rollback por teste, ligado pelo listener do micronaut-data), isso produzia duas classes de bug determinístico:

  • Deadlock: um inserir via repo junta a tx-de-teste e fica não-commitado segurando o lock do índice único; uma 2ª conexão crua no mesmo teste tentando o conflito bloqueia nesse lock — e a 1ª não commita porque a thread está presa na 2ª. Sem lock_timeout, trava eterna (o gate docker nunca completava — sempre pendurava no constraintUnica).
  • Null-read: escrita via serviço/repo @Transactional (não-commitada, conexão A) lida por uma conexão crua separada (B) que não enxerga o não-commitado → null.

Decisão

  1. Provisionamento pela casa: adotar PostgresTestResource + PostgresTestPropertyProvider do xadm-comum-teste (ADR 0019) — um postgres:18-alpine (Testcontainers) compartilhado por JVM, injetado por classe (implements PostgresTestPropertyProvider + @TestInstance(PER_CLASS)). Remover o plugin io.micronaut.test-resources e o bloco testResources{}. Bônus: como o banco passa a ser opt-in por classe, some a complexidade do split (o plugin configurava toda task Test) — o test (POJO) nunca sobe Docker sem acoplamento a desfazer.
  2. @MicronautTest(transactional = false) em todo teste de integração: cada write commita pela sua conexão, então servidor embarcado e repositórios leem o dado semeado — mata as duas classes de bug na raiz.
  3. Zero SQL cru no corpo dos testes: seed e leitura vão pelos repositórios de produção (mesmo caminho de escrita do runtime). O de-baixo-nível que o repo não expõe — limpeza entre testes, seed com timestamp no passado (poda por idade), contagem do outbox mensagem_m2m, atalhos de FSM, soft-delete, getters ausentes — vive num único apoio de teste, support/BancoFixture (@Singleton só do sourceSet integrationTest). A tabela mensagem_m2m (dona = xadm-mensageria) é tocada só pela API publicada do repo (deleteAll() herdado + listarRecentes() + filtro).

Alternativas descartadas

  • Manter JDBC cru + só transactional = false: resolve os bugs, mas deixa o padrão frágil (SQL espalhado em 20 arquivos, fácil reintroduzir a armadilha).
  • Métodos test-only nos repositórios de produção (apagarTudo, forcarNaFila, backdaters, soft-delete): inflaria a API de prod com setters que burlam FSM/invariante — e exigiria mexer no commons compartilhado (xadm-mensageria). Rejeitado por over-engineering (constituição). A BancoFixture concentra isso no sourceSet de teste, sem tocar produção.
  • Manter o plugin io.micronaut.test-resources: funciona, mas diverge do padrão house-wide (ADR 0019) sem ganho — o PostgresTestResource é equivalente e é o que os apps da casa usam.

Consequência

Suite de integração roda e passa de ponta a ponta (antes o gate docker travava), 100% via API da casa (xadm-comum-teste + repositórios), sem SQL cru fora da BancoFixture. Ver guia-do-código §Rodar e testar.