En este tema, aprenderemos cómo integrar Mockito con JUnit para crear pruebas unitarias más efectivas y robustas. Mockito es una biblioteca de Java que permite crear objetos simulados (mocks) para pruebas unitarias. Esto es especialmente útil cuando queremos aislar el código que estamos probando de sus dependencias.
Contenido
- ¿Qué es Mockito?
- Configuración de Mockito con JUnit
- Creando Mocks con Mockito
- Inyectando Mocks en el Código
- Verificando Interacciones
- Ejemplo Práctico
- Ejercicios Prácticos
- ¿Qué es Mockito?
Mockito es una biblioteca de Java que permite crear objetos simulados (mocks) para pruebas unitarias. Los mocks son objetos que imitan el comportamiento de objetos reales en un entorno controlado. Esto es útil para probar el comportamiento de una clase sin depender de sus dependencias externas.
- Configuración de Mockito con JUnit
Para usar Mockito con JUnit, primero necesitamos agregar las dependencias necesarias a nuestro proyecto. Si estás usando Maven, agrega las siguientes dependencias a tu archivo pom.xml
:
<dependencies> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.11.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.2</version> <scope>test</scope> </dependency> </dependencies>
Si estás usando Gradle, agrega las siguientes líneas a tu archivo build.gradle
:
dependencies { testImplementation 'org.mockito:mockito-core:3.11.2' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.7.2' }
- Creando Mocks con Mockito
Para crear un mock con Mockito, usamos el método Mockito.mock()
. Aquí hay un ejemplo básico:
import org.junit.jupiter.api.Test; import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; public class MyServiceTest { @Test public void testMyService() { // Crear un mock de la clase Dependency Dependency dependencyMock = Mockito.mock(Dependency.class); // Definir el comportamiento del mock Mockito.when(dependencyMock.someMethod()).thenReturn("Mocked Response"); // Usar el mock en la clase que estamos probando MyService myService = new MyService(dependencyMock); String result = myService.performAction(); // Verificar el resultado assertEquals("Mocked Response", result); } }
- Inyectando Mocks en el Código
En lugar de crear manualmente los mocks, podemos usar anotaciones para simplificar el proceso. Mockito proporciona la anotación @Mock
para crear mocks y @InjectMocks
para inyectarlos en la clase que estamos probando.
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; public class MyServiceTest { @Mock private Dependency dependencyMock; @InjectMocks private MyService myService; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); } @Test public void testMyService() { // Definir el comportamiento del mock when(dependencyMock.someMethod()).thenReturn("Mocked Response"); // Usar el mock en la clase que estamos probando String result = myService.performAction(); // Verificar el resultado assertEquals("Mocked Response", result); } }
- Verificando Interacciones
Además de definir el comportamiento de los mocks, también podemos verificar que ciertos métodos fueron llamados con los argumentos correctos. Esto se hace usando el método Mockito.verify()
.
import static org.mockito.Mockito.verify; @Test public void testMyService() { // Definir el comportamiento del mock when(dependencyMock.someMethod()).thenReturn("Mocked Response"); // Usar el mock en la clase que estamos probando String result = myService.performAction(); // Verificar el resultado assertEquals("Mocked Response", result); // Verificar que el método someMethod() fue llamado una vez verify(dependencyMock).someMethod(); }
- Ejemplo Práctico
Vamos a ver un ejemplo más completo que incluye la creación de mocks, la inyección de dependencias y la verificación de interacciones.
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class UserServiceTest { @Mock private UserRepository userRepositoryMock; @InjectMocks private UserService userService; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); } @Test public void testGetUser() { // Definir el comportamiento del mock User mockUser = new User("John", "Doe"); when(userRepositoryMock.findUserById(1)).thenReturn(mockUser); // Usar el mock en la clase que estamos probando User result = userService.getUser(1); // Verificar el resultado assertEquals("John", result.getFirstName()); assertEquals("Doe", result.getLastName()); // Verificar que el método findUserById() fue llamado una vez con el argumento 1 verify(userRepositoryMock).findUserById(1); } }
- Ejercicios Prácticos
Ejercicio 1: Crear y Usar Mocks
- Crea una clase
Calculator
con un métodoadd(int a, int b)
que devuelve la suma dea
yb
. - Crea una clase
CalculatorService
que tenga una dependencia deCalculator
. - Escribe una prueba unitaria para
CalculatorService
usando Mockito para simular el comportamiento deCalculator
.
Ejercicio 2: Verificar Interacciones
- Modifica la clase
CalculatorService
para que tenga un métodocalculateSum(int a, int b)
que use el métodoadd
deCalculator
. - Escribe una prueba unitaria para
calculateSum
que verifique que el métodoadd
deCalculator
fue llamado con los argumentos correctos.
Soluciones
Solución Ejercicio 1
// Calculator.java public class Calculator { public int add(int a, int b) { return a + b; } } // CalculatorService.java public class CalculatorService { private Calculator calculator; public CalculatorService(Calculator calculator) { this.calculator = calculator; } public int calculateSum(int a, int b) { return calculator.add(a, b); } } // CalculatorServiceTest.java import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; public class CalculatorServiceTest { @Mock private Calculator calculatorMock; @InjectMocks private CalculatorService calculatorService; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); } @Test public void testCalculateSum() { // Definir el comportamiento del mock when(calculatorMock.add(2, 3)).thenReturn(5); // Usar el mock en la clase que estamos probando int result = calculatorService.calculateSum(2, 3); // Verificar el resultado assertEquals(5, result); } }
Solución Ejercicio 2
import static org.mockito.Mockito.verify; @Test public void testCalculateSum() { // Definir el comportamiento del mock when(calculatorMock.add(2, 3)).thenReturn(5); // Usar el mock en la clase que estamos probando int result = calculatorService.calculateSum(2, 3); // Verificar el resultado assertEquals(5, result); // Verificar que el método add() fue llamado una vez con los argumentos 2 y 3 verify(calculatorMock).add(2, 3); }
Conclusión
En este tema, hemos aprendido cómo usar Mockito con JUnit para crear pruebas unitarias más efectivas. Hemos cubierto cómo configurar Mockito, crear mocks, inyectar mocks en el código, y verificar interacciones. Además, hemos visto un ejemplo práctico y proporcionado ejercicios para reforzar los conceptos aprendidos. Con estas herramientas, estarás mejor preparado para escribir pruebas unitarias robustas y mantener un código de alta calidad.
Curso de JUnit
Módulo 1: Introducción a JUnit
Módulo 2: Anotaciones Básicas de JUnit
- Entendiendo @Test
- Usando @Before y @After
- Usando @BeforeClass y @AfterClass
- Ignorando Tests con @Ignore
Módulo 3: Aserciones en JUnit
Módulo 4: Tests Parametrizados
- Introducción a los Tests Parametrizados
- Creando Tests Parametrizados
- Usando @ParameterizedTest
- Tests Parametrizados Personalizados
Módulo 5: Suites de Tests
Módulo 6: Mocking con JUnit
Módulo 7: Características Avanzadas de JUnit
Módulo 8: Mejores Prácticas y Consejos
- Escribiendo Tests Efectivos
- Organizando el Código de Tests
- Desarrollo Guiado por Tests (TDD)
- Integración Continua con JUnit