paint-brush
Pruebas en Java: los conceptos clave [Parte 1: Pruebas unitarias]por@gromspys
1,418 lecturas
1,418 lecturas

Pruebas en Java: los conceptos clave [Parte 1: Pruebas unitarias]

por Sergei Korneev10m2023/11/14
Read on Terminal Reader

Demasiado Largo; Para Leer

En este artículo, exploraremos los conceptos clave de las pruebas unitarias de Java, incluidos ejemplos de código simples, parametrización, pruebas de excepciones y anotaciones.
featured image - Pruebas en Java: los conceptos clave [Parte 1: Pruebas unitarias]
Sergei Korneev HackerNoon profile picture

Las pruebas unitarias son una práctica fundamental en el desarrollo de software que garantiza la confiabilidad y corrección de su código. En este artículo, exploraremos los conceptos clave de las pruebas unitarias de Java, incluidos ejemplos de código simples, parametrización, pruebas de excepciones, anotaciones como @Before , @BeforeEach , @After y @AfterEach , así como el uso de simulacros y apéndices. .


También presentaremos la popular biblioteca Mockito para burlarnos y brindaremos orientación sobre cómo medir la cobertura del código utilizando complementos de Maven.

La importancia de las pruebas unitarias

Las pruebas unitarias son la práctica de probar componentes individuales o unidades de código de forma aislada para verificar su corrección. El objetivo principal es detectar y corregir errores en las primeras etapas del proceso de desarrollo, garantizando que cada unidad de código se comporte como se espera.


Comencemos creando una clase de calculadora simple que podamos usar para nuestros ejemplos. La clase Calculator contiene operaciones 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; } }

Escribir una prueba unitaria simple

Para crear una prueba unitaria para nuestra clase Calculator , puede utilizar el marco JUnit, un marco de prueba popular para Java. Escribamos una prueba unitaria simple para el 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); } }

En esta prueba, importamos las clases JUnit necesarias y anotamos el método de prueba con @Test . Luego creamos una instancia de la clase Calculator y afirmamos que el resultado del método add es igual al valor esperado (5).

Pruebas parametrizadas

Las pruebas parametrizadas le permiten ejecutar la misma lógica de prueba con múltiples conjuntos de datos de entrada. Esto es útil para probar un método con varios valores de entrada. Para hacer esto en JUnit, puede usar la anotación @ParameterizedTest y proporcionar los valores de entrada y los resultados esperados como parámetros. He aquí un ejemplo:

 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); } }

En este ejemplo, la anotación @ParameterizedTest nos permite proporcionar múltiples conjuntos de valores de entrada y resultados esperados en formato CSV. El método de prueba se ejecuta para cada combinación, lo que garantiza la corrección del método add .

Pruebas de excepción

Probar las excepciones es fundamental para garantizar que su código maneje los errores de manera adecuada. Para probar casos de excepción, puede utilizar la anotación @Test junto con el método assertThrows proporcionado por JUnit.


He aquí un ejemplo:

 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)); } }

En esta prueba, verificamos que dividir un número entre cero en la clase Calculator arroja una ArithmeticException .

Uso de anotaciones: @Before, @BeforeEach, @After y @AfterEach

Anotaciones como @Before , @BeforeEach , @After y @AfterEach se utilizan para configurar y desmantelar el entorno de prueba. Estas anotaciones ayudan a gestionar las tareas comunes de inicialización y limpieza de pruebas.


  • @Before y @After se ejecutan una vez antes y después de todos los métodos de prueba en la clase de prueba, respectivamente.
  • @BeforeEach y @AfterEach se ejecutan antes y después de cada método de prueba.


He aquí un ejemplo:

 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); } }

En este ejemplo, el método setUp está anotado con @BeforeEach e inicializa el objeto Calculator antes de cada prueba. El método tearDown , anotado con @AfterEach , limpia los recursos después de cada prueba.

Simulacros y talones

Los simulacros y los resguardos son esenciales en las pruebas unitarias cuando se desea aislar el código que se está probando de dependencias externas, como bases de datos o servicios web. Le permiten simular el comportamiento de estas dependencias.


  • Mock: Un simulacro es un objeto que imita el comportamiento de un objeto real pero que está bajo tu control. Registra interacciones y le permite verificar que se llaman métodos específicos.


  • Stub: un stub proporciona respuestas predeterminadas a llamadas a métodos. Devuelve datos predefinidos y se utiliza para simular ciertos comportamientos de un sistema externo.


Ampliemos la clase Calculator para incluir un método que involucre una dependencia externa y luego creemos un código auxiliar para simular esa dependencia.

 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(); } }

En esta clase Calculator actualizada, performComplexCalculation se basa en un ExternalService para multiplicar y obtener un valor constante.


A continuación se explica cómo puede crear un código auxiliar para probar la clase Calculator sin depender del 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 } }

En esta prueba, ExternalService se incluye dentro del método de prueba mediante la creación de una clase anónima que anula los métodos necesarios. De esta manera, el método de prueba Calculator se ejecuta sin depender de la implementación real de ExternalService .


Los stubs son útiles para simular el comportamiento de sistemas externos o dependencias para aislar y probar la funcionalidad específica de una clase o método. Esto le permite controlar el comportamiento del stub y centrarse en la unidad bajo prueba sin la necesidad de servicios externos reales.

Presentamos Mockito para burlarse

Mockito es una biblioteca Java popular para crear y administrar objetos simulados. Digamos que tenemos una clase PaymentService que interactúa con una pasarela de pago externa. Podemos usar Mockito para crear un simulacro de la pasarela de pago:

 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); } }

En este ejemplo, creamos un PaymentGateway simulado y usamos el método when para definir su comportamiento. Luego llamamos al método processPayment en la clase PaymentService y verificamos que el método charge se llamó con los parámetros esperados.

Cobertura de código y complemento Maven

La cobertura de código mide el porcentaje de líneas de código, ramas o declaraciones que ejecutan sus pruebas unitarias. Le ayuda a identificar código no probado y áreas que pueden requerir pruebas adicionales.


Maven es una herramienta de compilación popular para proyectos Java y puede integrar el análisis de cobertura de código en su proyecto Maven utilizando complementos como JaCoCo. Aquí le mostramos cómo agregar JaCoCo a su proyecto:


  1. Abra el archivo pom.xml de su proyecto.
  2. Agregue la configuración del complemento 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>


Ejecute su compilación Maven con el siguiente comando:

 mvn clean verify


Después de ejecutar la compilación, puede encontrar el informe de cobertura del código en el archivo target/site/jacoco/index.html . Este informe proporciona información detallada sobre la cobertura de su código por sus pruebas unitarias.

Conclusión

Las pruebas unitarias de Java son una práctica esencial para garantizar la confiabilidad y corrección de su código. Con herramientas como JUnit y Mockito, puede escribir pruebas unitarias y simulacros efectivos para sus componentes.


Al integrar el análisis de cobertura de código con Maven y JaCoCo, puede asegurarse de que sus pruebas cubran una parte importante de su código base. Probar y mantener pruebas unitarias periódicamente le ayudará a producir aplicaciones Java robustas y de alta calidad.


En la siguiente parte de esta serie, exploraremos las pruebas de integración, un aspecto crítico de las pruebas de software que implica probar las interacciones entre diferentes componentes y servicios para garantizar que el sistema general funcione correctamente.


¡Estén atentos a la segunda parte, donde nos sumergiremos en el apasionante mundo de las pruebas de integración!