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
inserirvia 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ª. Semlock_timeout, trava eterna (o gate docker nunca completava — sempre pendurava noconstraintUnica). - 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¶
- Provisionamento pela casa: adotar
PostgresTestResource+PostgresTestPropertyProviderdoxadm-comum-teste(ADR 0019) — umpostgres:18-alpine(Testcontainers) compartilhado por JVM, injetado por classe (implements PostgresTestPropertyProvider+@TestInstance(PER_CLASS)). Remover o pluginio.micronaut.test-resourcese o blocotestResources{}. Bônus: como o banco passa a ser opt-in por classe, some a complexidade do split (o plugin configurava toda taskTest) — otest(POJO) nunca sobe Docker sem acoplamento a desfazer. @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.- 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(@Singletonsó do sourceSetintegrationTest). A tabelamensagem_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). ABancoFixtureconcentra 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 — oPostgresTestResourceé 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.