paint-brush
Teste em Java: os principais conceitos [Parte 1: Teste de unidade]por@gromspys
1,337 leituras
1,337 leituras

Teste em Java: os principais conceitos [Parte 1: Teste de unidade]

por Sergei Korneev10m2023/11/14
Read on Terminal Reader

Muito longo; Para ler

Neste artigo, exploraremos os principais conceitos de teste de unidade Java, incluindo exemplos de código simples, parametrização, teste de exceção e anotações.
featured image - Teste em Java: os principais conceitos [Parte 1: Teste de unidade]
Sergei Korneev HackerNoon profile picture

O teste unitário é uma prática fundamental no desenvolvimento de software que garante a confiabilidade e correção do seu código. Neste artigo, exploraremos os principais conceitos de teste de unidade Java, incluindo exemplos de código simples, parametrização, teste de exceção, anotações como @Before , @BeforeEach , @After e @AfterEach , bem como o uso de mocks e stubs .


Também apresentaremos a popular biblioteca Mockito para simulação e forneceremos orientação sobre como medir a cobertura de código usando plug-ins Maven.

A importância dos testes unitários

O teste de unidade é a prática de testar componentes individuais ou unidades de código isoladamente para verificar sua correção. O objetivo principal é detectar e corrigir bugs no início do processo de desenvolvimento, garantindo que cada unidade de código se comporte conforme o esperado.


Vamos começar criando uma classe de calculadora simples que podemos usar em nossos exemplos. A classe Calculator contém operações aritméticas básicas:

 public class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } }

Escrevendo um teste de unidade simples

Para criar um teste de unidade para nossa classe Calculator , você pode usar a estrutura JUnit, uma estrutura de teste popular para Java. Vamos escrever um teste de unidade simples para o método add :

 import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @Test public void testAdd() { Calculator calculator = new Calculator(); int result = calculator.add(2, 3); assertEquals(5, result); } }

Neste teste, importamos as classes JUnit necessárias e anotamos o método de teste com @Test . Em seguida, criamos uma instância da classe Calculator e afirmamos que o resultado do método add é igual ao valor esperado (5).

Testes parametrizados

Os testes parametrizados permitem executar a mesma lógica de teste com vários conjuntos de dados de entrada. Isto é útil para testar um método com vários valores de entrada. Para fazer isso no JUnit, você pode usar a anotação @ParameterizedTest e fornecer os valores de entrada e os resultados esperados como parâmetros. Aqui está um exemplo:

 import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @ParameterizedTest @CsvSource({"2, 3, 5", "4, 7, 11", "0, 0, 0"}) public void testAdd(int a, int b, int expected) { Calculator calculator = new Calculator(); int result = calculator.add(a, b); assertEquals(expected, result); } }

Neste exemplo, a anotação @ParameterizedTest nos permite fornecer vários conjuntos de valores de entrada e resultados esperados em formato CSV. O método de teste é executado para cada combinação, garantindo a correção do método add .

Teste de exceção

Testar exceções é crucial para garantir que seu código lide com os erros de maneira adequada. Para testar casos de exceção, você pode usar a anotação @Test junto com o método assertThrows fornecido pelo JUnit.


Aqui está um exemplo:

 javaCopy codeimport org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; public class CalculatorTest { @Test public void testDivideByZero() { Calculator calculator = new Calculator(); assertThrows(ArithmeticException.class, () -> calculator.divide(5, 0)); } }

Neste teste, verificamos que dividir um número por zero na classe Calculator gera uma ArithmeticException .

Usando anotações: @Before, @BeforeEach, @After e @AfterEach

Anotações como @Before , @BeforeEach , @After e @AfterEach são usadas para configurar e desmontar o ambiente de teste. Essas anotações ajudam no gerenciamento de tarefas comuns de inicialização e limpeza de testes.


  • @Before e @After são executados uma vez antes e depois de todos os métodos de teste na classe de teste, respectivamente.
  • @BeforeEach e @AfterEach são executados antes e depois de cada método de teste.


Aqui está um exemplo:

 import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { private Calculator calculator; @BeforeEach public void setUp() { calculator = new Calculator(); } @AfterEach public void tearDown() { calculator = null; } @Test public void testAdd() { int result = calculator.add(2, 3); assertEquals(5, result); } @Test public void testSubtract() { int result = calculator.subtract(5, 3); assertEquals(2, result); } }

Neste exemplo, o método setUp é anotado com @BeforeEach e inicializa o objeto Calculator antes de cada teste. O método tearDown , anotado com @AfterEach , limpa recursos após cada teste.

Simulações e stubs

Mocks e stubs são essenciais em testes unitários quando você deseja isolar o código que está sendo testado de dependências externas, como bancos de dados ou serviços web. Eles permitem simular o comportamento dessas dependências.


  • Mock: Um mock é um objeto que imita o comportamento de um objeto real, mas está sob seu controle. Ele registra interações e permite verificar se métodos específicos são chamados.


  • Stub: um stub fornece respostas prontas para chamadas de método. Ele retorna dados predefinidos e é usado para simular determinados comportamentos de um sistema externo.


Vamos expandir a classe Calculator para incluir um método que envolve uma dependência externa e então criar um stub para simular essa dependência.

 public class Calculator { private ExternalService externalService; public Calculator(ExternalService externalService) { this.externalService = externalService; } public int performComplexCalculation(int a, int b) { int result = externalService.multiply(a, b); return result + externalService.getConstant(); } }

Nesta classe Calculator atualizada, performComplexCalculation depende de um ExternalService para multiplicação e obtenção de um valor constante.


Veja como você pode criar um stub para testar a classe Calculator sem depender do ExternalService real.

 import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class CalculatorTest { @Test public void testPerformComplexCalculation() { // Create a stub for ExternalService ExternalService externalServiceStub = new ExternalService() { @Override public int multiply(int a, int b) { return a * b; } @Override public int getConstant() { return 5; // Stubbed constant value } }; // Create the Calculator with the stubbed ExternalService Calculator calculator = new Calculator(externalServiceStub); // Test the performComplexCalculation method int result = calculator.performComplexCalculation(2, 3); assertEquals(11, result); // 2*3 + 5 (constant) = 11 } }

Neste teste, ExternalService é inserido no método de teste criando uma classe anônima que substitui os métodos necessários. Dessa forma, o método de teste Calculator é executado sem depender da implementação real de ExternalService .


Stubs são úteis para simular o comportamento de sistemas externos ou dependências para isolar e testar a funcionalidade específica de uma classe ou método. Isso permite controlar o comportamento do stub e focar na unidade em teste sem a necessidade de serviços externos reais.

Apresentando Mockito para zombaria

Mockito é uma biblioteca Java popular para criar e gerenciar objetos simulados. Digamos que temos uma classe PaymentService que interage com um gateway de pagamento externo. Podemos usar o Mockito para criar uma simulação para o gateway de pagamento:

 import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; public class PaymentServiceTest { @Test public void testProcessPayment() { PaymentGateway paymentGateway = mock(PaymentGateway.class); PaymentService paymentService = new PaymentService(paymentGateway); when(paymentGateway.charge(100.0)).thenReturn(true); boolean result = paymentService.processPayment(100.0); assertTrue(result); verify(paymentGateway, times(1)).charge(100.0); } }

Neste exemplo, criamos um PaymentGateway simulado e usamos o método when para definir seu comportamento. Em seguida, chamamos o método processPayment na classe PaymentService e verificamos se o método charge foi chamado com os parâmetros esperados.

Cobertura de código e plug-in Maven

A cobertura de código mede a porcentagem de linhas de código, ramificações ou instruções executadas pelos testes de unidade. Ajuda a identificar códigos não testados e áreas que podem exigir testes adicionais.


Maven é uma ferramenta de construção popular para projetos Java, e você pode integrar a análise de cobertura de código em seu projeto Maven usando plug-ins como JaCoCo. Veja como adicionar JaCoCo ao seu projeto:


  1. Abra o arquivo pom.xml do seu projeto.
  2. Adicione a configuração do plugin JaCoCo Maven:
 <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build>


Execute sua compilação do Maven com o seguinte comando:

 mvn clean verify


Depois de executar a compilação, você pode encontrar o relatório de cobertura de código no arquivo target/site/jacoco/index.html . Este relatório fornece informações detalhadas sobre a cobertura do seu código pelos testes de unidade.

Conclusão

O teste de unidade Java é uma prática essencial para garantir a confiabilidade e a correção do seu código. Com ferramentas como JUnit e Mockito, você pode escrever testes de unidade e simulações eficazes para seus componentes.


Ao integrar a análise de cobertura de código com Maven e JaCoCo, você pode garantir que seus testes cubram uma parte significativa de sua base de código. Testar e manter testes de unidade regularmente o ajudará a produzir aplicativos Java robustos e de alta qualidade.


Na próxima parte desta série, exploraremos os testes de integração, um aspecto crítico dos testes de software que envolve testar as interações entre diferentes componentes e serviços para garantir que o sistema geral funcione corretamente.


Fique ligado na segunda parte, onde mergulharemos no emocionante mundo dos testes de integração!