W3docs

Introdução ao JUnit em Java

O que é JUnit, como adicioná-lo a um projeto Java e como escrever seu primeiro teste JUnit.

JUnit é o framework padrão de facto para escrever testes automatizados em Java. Um teste é apenas um pequeno método que executa um trecho do seu código e verifica que ele se comportou conforme o esperado; a função do JUnit é descobrir esses métodos, executar cada um de forma isolada, verificar as asserções e reportar quais passaram e quais falharam. A geração atual, JUnit 5 (também chamado de JUnit Jupiter), é distribuída como um conjunto de pequenas bibliotecas que você adiciona ao seu build — não faz parte do JDK — e alimenta a etapa mvn test / gradle test que controla o CI de quase todo projeto Java.

Por que usar um framework de testes

Você poderia verificar código manualmente com métodos main e println — mas isso escala mal. Um framework oferece quatro coisas que você teria que reconstruir por conta própria:

  • Descoberta — ele encontra automaticamente todo método @Test; você nunca mantém uma lista.
  • Isolamento — cada teste recebe um fixture novo, para que um teste não possa corromper outro.
  • Asserções — um vocabulário rico (assertEquals, assertThrows, …) que produz mensagens de falha precisas.
  • Relatórios — um resumo uniforme de aprovação/reprovação que a ferramenta de build e a IDE entendem.

Faça isso uma vez e os testes se tornam baratos de escrever, que é exatamente o ponto: testes baratos são escritos, e código testado pode ser alterado sem medo.

Adicionando JUnit a um projeto

JUnit 5 é uma dependência declarada no seu arquivo de build. Com Maven, o agregador junit-jupiter traz a API (para compilar) e o engine (para executar):

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <version>5.11.3</version>
  <scope>test</scope>
</dependency>

Com Gradle são duas linhas mais o switch useJUnitPlatform():

dependencies {
  testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3'
}
test {
  useJUnitPlatform()
}

Os fontes de teste ficam em src/test/java, espelhando o pacote da classe que estão testando. O escopo test mantém o JUnit fora do seu artefato de produção.

Seu primeiro teste

Um teste JUnit é um método comum anotado com @Test. Dentro dele você chama o código a ser testado e verifica o resultado com asserções. Aqui está uma Calculator e uma classe de teste para ela:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class CalculatorTest {

  private final Calculator calc = new Calculator();

  @Test
  void addReturnsSum() {
    assertEquals(5, calc.add(2, 3));
  }

  @Test
  void divideByZeroThrows() {
    assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
  }
}

Observe o padrão que todo teste segue — Arrange (organizar o fixture), Act (agir chamando o método), Assert (verificar o resultado). Os métodos de teste são package-private (sem public necessário no JUnit 5) e retornam void. Execute mvn test e o build fica verde apenas se todas as asserções passarem.

As anotações e asserções mais usadas

A superfície de API do JUnit é pequena. Esses poucos membros cobrem a grande maioria dos testes reais:

MembroPacote / classeFinalidade
@Testorg.junit.jupiter.apiMarca um método como teste
@BeforeEach / @AfterEachorg.junit.jupiter.apiExecuta antes/depois de cada teste (configurar / destruir fixtures)
@BeforeAll / @AfterAllorg.junit.jupiter.apiExecuta uma vez antes/depois de todos os testes da classe
@DisplayNameorg.junit.jupiter.apiUm nome legível para os relatórios
@Disabledorg.junit.jupiter.apiPula um teste temporariamente
assertEquals(exp, act)AssertionsFalha a menos que os dois sejam iguais
assertTrue / assertFalseAssertionsFalha a menos que um boolean seja verdadeiro/falso
assertThrows(type, exec)AssertionsFalha a menos que o lambda lance aquela exceção
assertNull / assertNotNullAssertionsFalha se a nulidade for incorreta

@BeforeEach é o que dá a cada teste um estado limpo — o JUnit constrói uma instância nova da classe de teste para cada @Test e então executa a configuração, então o estado nunca vaza entre os testes.

O que o JUnit faz por você, em um arquivo executável

O executor de código aqui não tem JUnit no classpath (é uma biblioteca externa, não faz parte do JDK), então o exemplo abaixo reimplementa o loop principal do JUnit em Java puro: um fixture recriado antes de cada teste, um pequeno conjunto de helpers assertXxx, uma lista de métodos de teste executados de forma independente e uma contagem de aprovações/reprovações no final. Essa é exatamente a maquinaria que o JUnit automatiza — vê-la exposta torna o framework real óbvio. Um teste falha de propósito para que você possa ver como fica o vermelho.

java— editable, runs on the server

O que extrair da execução:

  • Os três testes corretos imprimem PASS e o quarto imprime FAIL deliberatelyFailing -> expected <10> but was <5> — uma mensagem de falha precisa, não apenas "um teste falhou." Essa diferença (expected … but was …) é exatamente o que o assertEquals do JUnit fornece, e é o que torna um teste vermelho diagnosticável de relance.
  • setUp() executa antes de cada teste, então calc é uma Calculator nova a cada vez. Esse é o contrato do @BeforeEach: os testes são isolados, e a ordem em que são executados nunca pode importar porque nenhum deles compartilha estado mutável.
  • divideThrowsOnZero passa ao verificar que uma exceção é lançada — assertThrows torna "isso deve falhar" uma asserção positiva e de primeira classe, em vez de um try/catch frágil. Exceções esperadas são comportamento que vale a pena testar, não erros a serem engolidos.
  • O total final — Tests run: 4, Passed: 3, Failed: 1, Assertions: 5 — é o relatório. Um teste falhando de quatro ainda vira o build inteiro para RED; o CI trata qualquer falha como uma parada, o que é por isso que um conjunto verde é significativo.
  • Nada aqui importou JUnit, mas a forma é idêntica: descoberta de métodos anotados, configuração por teste, asserções, resumo. O valor do JUnit é que ele automatiza esse loop (e adiciona descoberta, paralelismo, testes parametrizados e integração com a IDE) para que você escreva apenas os corpos dos testes.

O que o restante desta parte aborda

Esta parte se baseia no loop principal que você acabou de ver:

  • Anotações do JUnit@Test, @DisplayName, @Disabled e o restante do conjunto de marcadores.
  • O ciclo de vida do teste — como @BeforeEach / @AfterEach / @BeforeAll / @AfterAll dão a cada teste um fixture limpo.
  • Asserções — o catálogo completo de assertXxx, incluindo assertThrows para testes de exceção.
  • Testes parametrizados — executar um corpo de teste sobre muitas entradas em vez de copiar casos.
  • Mocking com Mockito — substituir os colaboradores de uma classe por substitutos para que um teste unitário permaneça um teste unitário.

Se você quiser ter uma visão geral de por que os testes automatizados são importantes antes de mergulhar na API, veja Testes em Java. Caso contrário, o próximo capítulo começa onde toda suite começa: definindo uma classe de teste e executando-a.

Prática

Prática
No JUnit 5, o que a anotação @BeforeEach em um método garante sobre o fixture de teste?
No JUnit 5, o que a anotação @BeforeEach em um método garante sobre o fixture de teste?
Was this page helpful?