1. La idea
Un caso de uso no debería necesitar una base de datos, una API o un servicio externo para demostrar que su propia lógica funciona.
class CrearUsuario {
private final UsuarioRepository repository;
CrearUsuario(UsuarioRepository repository) {
this.repository = repository;
}
void ejecutar(String nombre) {
repository.guardar(new Usuario(nombre));
}
}
El objetivo de una prueba unitaria es comprobar la lógica de CrearUsuario, no comprobar si MySQL, una API externa o cualquier otro servicio funciona correctamente.
2. La dependencia real
En producción usamos la implementación verdadera:
UsuarioRepository repository = new MySQLUsuarioRepository();
CrearUsuario casoDeUso = new CrearUsuario(repository);
CrearUsuario depende de UsuarioRepository. Esta dependencia representa un componente externo al código que queremos probar.
Si usamos la implementación real durante el test, la prueba podría acabar dependiendo de una base de datos, una configuración, la red u otros elementos externos.
3. Mock
Un Mock es un objeto simulado que reemplaza una dependencia real durante una prueba.
UsuarioRepository mockRepository = mock(UsuarioRepository.class);
CrearUsuario casoDeUso = new CrearUsuario(mockRepository);
casoDeUso.ejecutar("Arturo");
verify(mockRepository).guardar(any(Usuario.class));
El mock no intenta guardar realmente el usuario en MySQL. Su función es permitirnos controlar y observar la interacción con UsuarioRepository.
Idea clave: Mock = objeto simulado que reemplaza una dependencia real.
4. Stub
Un Stub permite definir respuestas de antemano para controlar lo que ocurre durante una prueba.
Por ejemplo, podemos decirle a una dependencia simulada que devuelva un usuario específico:
when(mockRepository.buscarPorNombre("Arturo"))
.thenReturn(new Usuario("Arturo"));
Cuando el código llame a buscarPorNombre("Arturo"), recibirá exactamente el resultado que configuramos.
Idea clave: Stub = respuesta predefinida para controlar el comportamiento durante la prueba.
5. Spy
Un Spy permite observar cómo se usó un objeto durante una prueba.
UsuarioRepository spyRepository = spy(usuarioRepository);
CrearUsuario casoDeUso = new CrearUsuario(spyRepository);
casoDeUso.ejecutar("Arturo");
verify(spyRepository).guardar(any(Usuario.class));
A diferencia de un mock completamente simulado, un spy puede usar el comportamiento real del objeto mientras permite observar sus interacciones.
Idea clave: Spy = observar cómo se usó un objeto durante la prueba.
6. Verify
Verify comprueba que una interacción ocurrió tal como esperábamos.
casoDeUso.ejecutar("Arturo");
verify(mockRepository).guardar(any(Usuario.class));
En este ejemplo comprobamos que CrearUsuario realmente llamó al método guardar del repositorio.
También podemos comprobar determinadas condiciones de la interacción:
verify(mockRepository, times(1))
.guardar(any(Usuario.class));
Idea clave: Verify = comprobar que una interacción ocurrió como se esperaba.
7. Dependency
Una Dependency es un componente del que nuestro código necesita ayuda para realizar su trabajo.
En este ejemplo:
class CrearUsuario {
private final UsuarioRepository repository;
CrearUsuario(UsuarioRepository repository) {
this.repository = repository;
}
}
UsuarioRepository es una dependencia de CrearUsuario.
Durante una prueba podemos reemplazar esa dependencia real por un mock:
UsuarioRepository mockRepository = mock(UsuarioRepository.class);
CrearUsuario casoDeUso = new CrearUsuario(mockRepository);
Idea clave: Dependency = componente externo que nuestro código necesita para funcionar.
8. Cómo se relacionan
Estos conceptos forman parte del mismo proceso:
Dependency → Mock → Stub → ejecución → Verify
Tenemos una dependencia real, la reemplazamos por un mock, configuramos respuestas cuando sea necesario mediante stubs, ejecutamos el código que queremos probar y finalmente verificamos las interacciones.
Un spy puede servir cuando queremos conservar parte del comportamiento real y observar las interacciones a la vez.
9. Ejemplo completo
@Test
void debeCrearUsuario() {
UsuarioRepository mockRepository = mock(UsuarioRepository.class);
CrearUsuario casoDeUso = new CrearUsuario(mockRepository);
casoDeUso.ejecutar("Arturo");
verify(mockRepository).guardar(any(Usuario.class));
}
En este test:
UsuarioRepositoryes la Dependency.mockRepositoryes el Mock.when(...).thenReturn(...)sería un Stub si necesitamos controlar una respuesta.verify(...)comprueba la interacción.- Un Spy sería otra alternativa cuando queremos observar un objeto manteniendo su comportamiento real.
10. La idea que hay que recordar
Mocking = reemplazar una dependencia para probar el comportamiento de aquello que estamos testeando.
El objetivo no es comprobar que MySQL funciona. El objetivo es comprobar que CrearUsuario funciona correctamente y que interactúa con sus dependencias de la manera esperada.
Lección aprendida
Un buen test unitario reduce el mundo a lo estrictamente necesario: código real, dependencias controladas y comportamiento verificable.
Las palabras clave que debes recordar son: Mock, Stub, Spy, Verify y Dependency.
© Arturo Juárez Monroy