Quiz: Tests Spring Boot
Ponga a prueba sus conocimientos sobre JUnit 5, Mockito y pruebas de integración.
30 preguntas - Haga clic en cada pregunta para revelar la respuesta.
Preguntas 1-10: JUnit 5
Pregunta 1: ¿Cuáles son las anotaciones básicas de JUnit 5?
Respuesta:
| Anotación | Descripción |
|---|---|
@Test | Marca un método de prueba |
@BeforeEach | Antes de cada prueba |
@AfterEach | Después de cada prueba |
@BeforeAll | Antes de todas las pruebas (static) |
@AfterAll | Después de todas las pruebas (static) |
@Disabled | Desactiva una prueba |
@DisplayName | Nombre legible |
Pregunta 2: ¿Cómo estructurar una prueba con el patrón AAA?
Respuesta: Arrange-Act-Assert:
@Test
void shouldCalculateTotal() {
// Arrange (Given)
Order order = new Order();
order.addItem(new Item("Book", 10.0));
order.addItem(new Item("Pen", 2.0));
// Act (When)
double total = order.calculateTotal();
// Assert (Then)
assertEquals(12.0, total);
}
Pregunta 3: ¿Qué aserciones usar con AssertJ?
Respuesta:
import static org.assertj.core.api.Assertions.*;
assertThat(result).isEqualTo(expected);
assertThat(list).hasSize(3);
assertThat(list).contains("item");
assertThat(user.getName()).isNotBlank();
assertThat(price).isGreaterThan(0);
assertThatThrownBy(() -> service.process(null))
.isInstanceOf(IllegalArgumentException.class);
Pregunta 4: ¿Cómo probar una excepción?
Respuesta:
// JUnit 5
@Test
void shouldThrowException() {
assertThrows(IllegalArgumentException.class, () -> {
service.process(null);
});
}
// AssertJ
@Test
void shouldThrowException() {
assertThatThrownBy(() -> service.process(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("cannot be null");
}
Pregunta 5: ¿Cómo ejecutar una prueba varias veces con diferentes parámetros?
Respuesta:
@ParameterizedTest
@ValueSource(strings = {"a@b.com", "test@domain.org"})
void shouldValidateEmail(String email) {
assertTrue(validator.isValidEmail(email));
}
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"5, 5, 10",
"0, 0, 0"
})
void shouldAdd(int a, int b, int expected) {
assertEquals(expected, calculator.add(a, b));
}
Pregunta 6: ¿Cómo agrupar las pruebas?
Respuesta:
@Nested
@DisplayName("When user is logged in")
class WhenLoggedIn {
@BeforeEach
void setup() {
login();
}
@Test
void canAccessProfile() { }
@Test
void canLogout() { }
}
@Nested
@DisplayName("When user is anonymous")
class WhenAnonymous {
@Test
void cannotAccessProfile() { }
}
Pregunta 7: ¿Cómo desactivar una prueba condicionalmente?
Respuesta:
@Test
@EnabledOnOs(OS.LINUX)
void onlyOnLinux() { }
@Test
@EnabledIfEnvironmentVariable(named = "ENV", matches = "CI")
void onlyInCI() { }
@Test
@DisabledIf("isWeekend")
void notOnWeekend() { }
boolean isWeekend() {
return LocalDate.now().getDayOfWeek().getValue() > 5;
}
Pregunta 8: ¿Cómo establecer un timeout a una prueba?
Respuesta:
@Test
@Timeout(value = 5, unit = TimeUnit.SECONDS)
void shouldCompleteInTime() {
// Prueba que debe finalizar en menos de 5 segundos
}
// O con assertTimeout
@Test
void shouldCompleteInTime() {
assertTimeout(Duration.ofSeconds(2), () -> {
service.longOperation();
});
}
Pregunta 9: ¿Cómo reutilizar la configuración entre pruebas?
Respuesta:
// Clase base
abstract class BaseTest {
@BeforeEach
void commonSetup() {
// Configuración común
}
}
class UserServiceTest extends BaseTest {
@Test
void testUser() { }
}
// O con Extension
@ExtendWith(DatabaseExtension.class)
class DatabaseTest { }
Pregunta 10: ¿Cómo ejecutar las pruebas en un orden específico?
Respuesta:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class OrderedTest {
@Test
@Order(1)
void createUser() { }
@Test
@Order(2)
void updateUser() { }
@Test
@Order(3)
void deleteUser() { }
}
Nota: ¡evite en lo posible las pruebas dependientes!
Preguntas 11-20: Mockito
Pregunta 11: ¿Cómo crear un mock con Mockito?
Respuesta:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
// O manualmente
UserRepository repo = mock(UserRepository.class);
}
Pregunta 12: ¿Cómo definir el comportamiento de un mock?
Respuesta:
// Devolver un valor
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
// Lanzar una excepción
when(userRepository.findById(999L)).thenThrow(new NotFoundException());
// Respuesta dinámica
when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
User u = invocation.getArgument(0);
u.setId(1L);
return u;
});
Pregunta 13: ¿Cómo verificar que se llamó a un método?
Respuesta:
// Verificar la llamada
verify(userRepository).save(any(User.class));
// Verificar el número de llamadas
verify(userRepository, times(2)).findById(anyLong());
verify(userRepository, never()).delete(any());
verify(userRepository, atLeast(1)).findAll();
Pregunta 14: ¿Cuál es la diferencia entre @Mock y @Spy?
Respuesta:
@Mock: crea un objeto falso, todos los métodos devuelven null/0/false@Spy: utiliza el objeto real, puede sobreescribir algunos métodos
@Spy
private UserService userService = new UserService();
// Solo este método es mockeado
doReturn(user).when(userService).findById(1L);
// Los demás métodos ejecutan el código real
Pregunta 15: ¿Cómo capturar los argumentos pasados a un mock?
Respuesta:
@Captor
ArgumentCaptor<User> userCaptor;
@Test
void shouldCaptureUser() {
userService.createUser(request);
verify(userRepository).save(userCaptor.capture());
User savedUser = userCaptor.getValue();
assertThat(savedUser.getEmail()).isEqualTo("test@example.com");
}
Pregunta 16: ¿Cómo mockear un método void?
Respuesta:
// No hacer nada (por defecto)
doNothing().when(emailService).sendEmail(any());
// Lanzar una excepción
doThrow(new MailException()).when(emailService).sendEmail(any());
// Ejecutar código
doAnswer(invocation -> {
String email = invocation.getArgument(0);
log.info("Would send email to: {}", email);
return null;
}).when(emailService).sendEmail(any());
Pregunta 17: ¿Cómo mockear métodos estáticos?
Respuesta:
@Test
void testStaticMethod() {
try (MockedStatic<UUID> mockedUUID = mockStatic(UUID.class)) {
UUID fakeUUID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
mockedUUID.when(UUID::randomUUID).thenReturn(fakeUUID);
// Prueba
String id = service.generateId();
assertThat(id).contains("123e4567");
}
}
Pregunta 18: ¿Cómo verificar el orden de las llamadas?
Respuesta:
@Test
void shouldCallInOrder() {
service.process();
InOrder inOrder = inOrder(validator, repository, notifier);
inOrder.verify(validator).validate(any());
inOrder.verify(repository).save(any());
inOrder.verify(notifier).notify(any());
}
Pregunta 19: ¿Cómo usar los matchers de Mockito?
Respuesta:
when(repo.findByName(anyString())).thenReturn(user);
when(repo.findByAge(eq(25))).thenReturn(users);
when(repo.search(argThat(s -> s.length() > 3))).thenReturn(results);
verify(repo).save(argThat(user ->
user.getEmail().contains("@") && user.getAge() > 18
));
Pregunta 20: ¿Cómo reinicializar un mock entre pruebas?
Respuesta:
@AfterEach
void resetMocks() {
reset(userRepository);
}
// O para un mock específico
Mockito.clearInvocations(userRepository);
Preguntas 21-30: Pruebas de Integración
Pregunta 21: ¿Qué anotación se usa para una prueba de integración en Spring Boot?
Respuesta:
@SpringBootTest
class IntegrationTest {
// Carga todo el contexto de Spring
}
// Con servidor web
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WebIntegrationTest { }
Pregunta 22: ¿Cómo probar solo la capa web?
Respuesta:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void shouldReturnUser() throws Exception {
when(userService.findById(1L)).thenReturn(user);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"));
}
}
Pregunta 23: ¿Cómo probar solo la capa repository?
Respuesta:
@DataJpaTest
class UserRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@Test
void shouldFindByEmail() {
User user = new User("test@example.com");
entityManager.persist(user);
entityManager.flush();
Optional<User> found = userRepository.findByEmail("test@example.com");
assertThat(found).isPresent();
}
}
Pregunta 24: ¿Cómo usar TestRestTemplate?
Respuesta:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldCreateUser() {
UserRequest request = new UserRequest("test@example.com");
ResponseEntity<User> response = restTemplate.postForEntity(
"/api/users", request, User.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
}
}
Pregunta 25: ¿Cómo usar una base de datos de prueba?
Respuesta:
// H2 en memoria (automático con @DataJpaTest)
// O Testcontainers
@Testcontainers
@SpringBootTest
class PostgresIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
}
Pregunta 26: ¿Cómo probar con un perfil específico?
Respuesta:
@SpringBootTest
@ActiveProfiles("test")
class TestProfileTest {
// Usa application-test.yml
}
# application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
Pregunta 27: ¿Cómo probar la seguridad?
Respuesta:
@WebMvcTest(SecuredController.class)
@Import(SecurityConfig.class)
class SecurityTest {
@Test
void anonymousCannotAccess() throws Exception {
mockMvc.perform(get("/api/admin"))
.andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(roles = "ADMIN")
void adminCanAccess() throws Exception {
mockMvc.perform(get("/api/admin"))
.andExpect(status().isOk());
}
}
Pregunta 28: ¿Cómo probar las validaciones?
Respuesta:
@Test
void shouldRejectInvalidEmail() throws Exception {
String invalidRequest = """
{"email": "not-an-email", "name": "John"}
""";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(invalidRequest))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.errors.email").exists());
}
Pregunta 29: ¿Cómo medir la cobertura de código?
Respuesta: Con JaCoCo:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</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>
Reporte en target/site/jacoco/index.html
Pregunta 30: ¿Cuáles son las buenas prácticas de pruebas?
Respuesta:
- Una prueba = un comportamiento: no use aserciones múltiples y complejas
- Pruebas independientes: sin dependencias entre pruebas
- Nombres descriptivos:
shouldReturnUserWhenValidId - Datos aislados: cada prueba prepara sus propios datos
- Mockear dependencias externas: API, email, etc.
- Evitar pruebas inestables (flaky): sin dependencias de tiempo
- Probar casos de error: no solo el happy path
- Cobertura razonable: apunte a 70-80%, no necesariamente 100%
Puntuación
| Puntuación | Nivel |
|---|---|
| 27-30 | Excelente - Dominio de las pruebas |
| 21-26 | Bien - Algunas revisiones |
| 15-20 | Medio - Repase los capítulos |
| Menos de 15 | A revisar |