JUnit 5, Mockito e testes com Spring — conceitos essenciais explicados a fundo.
Por que limpar os dados de teste no início compensa mais do que no fim, por que bancos em memória ainda não pertencem a testes de integração apesar de o Testcontainers mudar o trade-off do overhead de containers, e por que leituras e repositórios merecem uma régua de qualidade mais baixa do que escritas.
Por que o schema e os dados de referência pertencem ao controle de versão, por que a entrega baseada em migração vence a baseada em estado assim que dados reais existem (o argumento de movimentação de dados), e por que testes de integração precisam de uma transação separada por seção de arrange/act/assert em vez de reutilizar uma única sessão.
Por que a reutilização de fixtures baseada em construtor/@BeforeEach acopla os testes entre si e prejudica a legibilidade, por que métodos de fábrica privados (Object Mother) são o padrão melhor, quando recorrer a @ParameterizedTest e @MethodSource, e como o estilo fluente do AssertJ evita a armadilha da ordem de argumentos expected/actual.
As regras reais do padrão AAA além de \"use três seções\": nunca escreva múltiplas seções de act ou um if dentro de um teste, trate uma seção de act com mais de uma linha como um mau cheiro no código de produção e não um problema do teste, e rejeite a nomenclatura rígida MethodUnderTest_Scenario_Result em favor de nomes de teste em inglês simples.
Regras concretas, às vezes contra-intuitivas, para usar mocks bem, além da distinção mock-vs-stub: mocks pertencem só a testes de integração, nunca a testes unitários de lógica de domínio pura; spies escritos à mão costumam vencer o verify() de um mock genérico; asserções precisam checar a contagem de chamadas, não só a ocorrência; e você só deveria mockar tipos que sua própria base de código define.
Por que testar métodos privados, expor estado privado, reimplementar a lógica do SUT em um teste, poluir o código de produção com switches só para teste, mockar classes concretas, e chamar o relógio do sistema diretamente remontam todos à mesma causa raiz, do Khorikov, Capítulo 11 \"Unit testing anti-patterns\".
Aprenda o padrão functional-core/imperative-shell para tirar decisões de negócio de código carregado de efeitos colaterais, tornando a lógica de decisão trivialmente testável por saída com zero mocks, e entenda os custos reais: um shell que ainda precisa de testes de integração, overhead de alocação por imutabilidade em caminhos quentes, e lógica de domínio que nem sempre pode ser separada de forma limpa.
As três formas de um teste unitário verificar comportamento: checar uma saída retornada, checar o estado resultante, ou checar uma chamada feita a um colaborador, e por que o teste baseado em saída produz os testes de mais alta qualidade mas só funciona para código livre de efeitos colaterais, deixando o teste baseado em estado como o padrão razoável e o teste baseado em comunicação (mocks) como a exceção rara.
Um mock (comando de saída) e um stub (consulta de entrada) respondem a perguntas diferentes, e afirmar sobre interações com um stub é um erro clássico de overspecification. O princípio mais profundo: mocking em si não causa testes frágeis, mockar uma interação que é um detalhe de implementação (intra-sistema, sem objetivo de cliente por trás) em vez de comportamento observável (uma chamada que cruza a fronteira da aplicação e permanece visível fora dela) causa. A correção não é evitar mocks, é mockar apenas em verdadeiras fronteiras do sistema.
Por que um teste de integração deve acessar um banco de dados real (uma dependência gerenciada) mas mockar um servidor SMTP ou barramento de mensagens (uma dependência não gerenciada), e por que mockar uma dependência gerenciada quebra silenciosamente a resistência à refatoração.
Duas definições genuinamente diferentes de \"isolamento\" em teste: mockar todo colaborador (London) versus mockar só dependências compartilhadas/voláteis (clássica), e por que discordam sobre o que é uma unidade.
Um mapa de código de produção por complexidade/significância de domínio e número de colaboradores: onde o teste unitário compensa mais, onde é desperdício, e como o padrão Humble Object corrige o código que é genuinamente difícil de testar.
Proteção contra regressões, resistência à refatoração, feedback rápido e manutenibilidade: os quatro atributos que julgam qualquer teste automatizado, e por que nenhum teste consegue maximizar os quatro ao mesmo tempo.
Como distribuir testes entre níveis: muitos testes unitários rápidos na base, cada vez menos testes de integração/sistema/aceitação em direção ao topo, o que testar em cada nível, e qual ferramenta se encaixa em cada um, do JUnit in Action, Third Edition, Cap. 22.
Escrevendo especificações de comportamento em Gherkin legível pelo negócio (Given/When/Then) e ligando-as a step definitions Java com Cucumber, com a configuração atual io.cucumber + JUnit 5 Platform substituindo o info.cukes/@RunWith(Cucumber.class) obsoleto do livro, do JUnit in Action, Third Edition, Cap. 21.
O ciclo red-green-refactor: escreva primeiro um teste que falha, adicione o menor código para passá-lo, então refatore com segurança, conduzindo o design com JUnit 5, do JUnit in Action, Third Edition, Cap. 20.
Testando uma camada de persistência JPA/Hibernate: mapeamento de entidade, persistence.xml, e queries de EntityManager contra um banco de dados de teste, com a mudança crítica de pacote javax para jakarta (Hibernate 6 / Spring 6), a fatia @DataJpaTest, e o Testcontainers para testes de integração com banco de dados real, do JUnit in Action, Third Edition, Cap. 19.4-19.6.
Testando código de acesso a dados contra um banco de dados em memória, do JDBC puro (Connection/PreparedStatement/ResultSet) ao JdbcTemplate/NamedParameterJdbcTemplate do Spring, com notas sobre o carregamento obsoleto de driver via Class.forName, o JdbcTemplate injetado em vez do JdbcDaoSupport, e a fatia @JdbcTest, do JUnit in Action, Third Edition, Cap. 19.1-19.3.
Controlando um controller REST do Spring pelo MockMvc: requisições HTTP simuladas, asserções de status/content-type/jsonPath, e uma camada de dados mockada, com a fatia @WebMvcTest de hoje, o @MockitoBean (substituindo @MockBean), e a mudança do NestedServletException no Spring 6, do JUnit in Action, Third Edition, Cap. 18.
Inicializando um contexto de aplicação completo com @SpringBootTest: varredura de componentes, auto-configuração e autowiring de beans, além das fatias de teste mais rápidas de hoje (@WebMvcTest, @DataJpaTest) e a mudança @MockBean para @MockitoBean, do JUnit in Action, Third Edition, Cap. 17.
Carregando um ApplicationContext do Spring em um teste JUnit 5 com @ExtendWith(SpringExtension.class) e @ContextConfiguration, injetando beans via @Autowired, com uma nota sobre o atalho @SpringJUnitConfig de hoje, do JUnit in Action, Third Edition, Cap. 16.
Controlando um navegador real pela interface WebDriver: busca de elementos, testes parametrizados entre navegadores, e o modelo Page Object, com notas sobre o que mudou do Selenium 3 do livro para o Selenium 4 de hoje, do JUnit in Action, Third Edition, Cap. 15.
Os cinco pontos de extensão (execução condicional, callbacks de ciclo de vida, resolução de parâmetros, tratamento de exceções, pós-processamento de instância) por trás do @ExtendWith, o mesmo mecanismo que MockitoExtension e SpringExtension usam, do JUnit in Action, Third Edition, Cap. 14.
Stubs vs. mocks como duas estratégias para falsificar um colaborador, e como criar e verificar mocks com Mockito, do JUnit in Action, Third Edition, Cap. 7-8.
Medindo cobertura com JaCoCo, escrevendo código testável (Lei de Demeter, composição em vez de condicionais), e como TDD/BDD/teste de mutação se encaixam no ciclo de desenvolvimento, do JUnit in Action, Third Edition, Cap. 6.
Teste unitário vs. integração vs. sistema vs. aceitação, e caixa-preta vs. caixa-branca: o vocabulário por trás de como uma suíte de testes é organizada, do JUnit in Action, Third Edition, Cap. 5.
Como Platform, Jupiter e Vintage se encaixam para rodar testes novos e legados lado a lado, mais uma referência rápida de migração de JUnit 4 para 5, do JUnit in Action, Third Edition, Cap. 3.1/3.3 (e uma nota curta do Cap. 4).
O ciclo de vida de teste do JUnit 5, asserções vs. suposições, testes aninhados/marcados, e testes parametrizados/dinâmicos, do JUnit in Action, Third Edition, Cap. 2.