====== Tests unitarios y de integración ======
{{ testing.png }}
Testing (Fuente: https://katalon.com/resources-center/blog/unit-testing)
----
===== Tests unitarios =====
==== Utils ====
Además de las capas ''Service'' y ''Controller'', vamos a suponer también que tenemos algunos métodos de utilidad, por ejemplo para trabajar con fechas. Asi también veremos cómo preparar los test unitarios para esta parte del código.
public class DateUtil {
public static final String DATE_PATTERN = "dd/MM/yyyy";
public static LocalDate format(String date) {
return LocalDate.parse(date, DateTimeFormatter.ofPattern(DATE_PATTERN));
}
public static long getDaysBetweenDates(LocalDate startDate, LocalDate endDate) {
return Math.abs(startDate.until(endDate, ChronoUnit.DAYS));
}
}
Asi, para las dos funciones anteriores, podríamos preparar algunos casos que nos permitieran comprobar cómo funciona para diferentes entradas.
public class DateUtilTests {
@Test
public void testFormat() {
String dateString = "12/04/2025";
LocalDate actualLocalDate = DateUtil.format(dateString);
assertEquals(2025, actualLocalDate.getYear());
assertEquals(4, actualLocalDate.getMonthValue());
assertEquals(12, actualLocalDate.getDayOfMonth());
}
@Test
public void testGetDaysBetweenDates() {
LocalDate startDate = LocalDate.of(2025, 1, 1);
LocalDate endDate = LocalDate.of(2025, 3, 1);
long actualDays = DateUtil.getDaysBetweenDates(startDate, endDate);
assertEquals(59, actualDays);
actualDays = DateUtil.getDaysBetweenDates(endDate, startDate);
assertEquals(59, actualDays);
actualDays = DateUtil.getDaysBetweenDates(startDate, startDate);
assertEquals(0, actualDays);
}
}
==== Capa Service ====
@Service
public class CarService {
. . .
public List getAll(String brand, String model) {
List carList;
if (brand.isEmpty() && model.isEmpty()) {
carList = carRepository.findAll();
} else if (brand.isEmpty()) {
carList = carRepository.findByModel(model);
} else if (model.isEmpty()) {
carList = carRepository.findByBrand(brand);
} else {
carList = carRepository.findByBrandAndModel(brand, model);
}
return modelMapper.map(carList, new TypeToken>() {}.getType());
}
. . .
}
En este caso tenemos el método ''getAll'' que toma 2 parámetros para filtrar el listado de vehículos por ''brand'' y/o ''model'', por lo que deberíamos preparar varios casos, uno por cada posibilidad. Sería algo como lo siguiente:
* ''getAll("", "")''
* ''getAll(brand, "")''
* ''getAll("", model)''
* ''getAll(brand, model)''
Para lo que necesitemos de la capa Repository, usaremos un objeto ''Mock'' para el que prepararemos nosotros mismos las respuestas según el caso. Algo parecido haremos también para el Bean de ''ModelMapper''
@ExtendWith(MockitoExtension.class)
public class CarServiceTests {
@InjectMocks
private CarService carService;
@Mock
private CarRepository carRepository;
@Mock
private ModelMapper modelMapper;
@Mock
private UserRepository userRepository;
@Test
public void testGetAll() {
List mockCarList = List.of(
new Car(1, "Opel", "Corsa", LocalDate.now(), LocalDate.now(), 200, 0.5, 0.34, null),
new Car(2, "Ford", "Fiesta", LocalDate.now(), LocalDate.now(), 120, 0.5, 0.34, null),
new Car(3, "Ford", "Ka", LocalDate.now(), LocalDate.now(), 100 , 0.2, 0.56, null)
);
List mockCarOutDtoList = List.of(
new CarOutDto(1, "Opel", "Corsa", 1, 0.5, 0.34),
new CarOutDto(2, "Ford", "Fiesta", 1, 0.5, 0.34),
new CarOutDto(3, "Ford", "Ka", 2, 0.2, 0.56)
);
// Mocks
when(carRepository.findAll()).thenReturn(mockCarList);
when(modelMapper.map(mockCarList, new TypeToken>() {}.getType())).thenReturn(mockCarOutDtoList);
// Listado de coches sin aplicar ningún filtro
List carList = carService.getAll("", "");
assertEquals(3, carList.size());
assertEquals("Opel", carList.get(0).getBrand());
assertEquals("Corsa", carList.get(0).getModel());
assertEquals("Ford", carList.get(carList.size() - 1).getBrand());
assertEquals("Ka", carList.get(carList.size() - 1).getModel());
verify(carRepository, times(1)).findAll();
verify(carRepository, times(0)).findByBrand("");
verify(carRepository, times(0)).findByModel("");
verify(carRepository, times(0)).findByBrandAndModel("", "");
}
@Test
public void testGetAllByBrand() {
List mockCarList = List.of(
new Car(1, "Opel", "Corsa", LocalDate.now(), LocalDate.now(), 200, 0.5, 0.34, null)
);
List mockCarOutDtoList = List.of(
new CarOutDto(1, "Opel", "Corsa", 1, 0.5, 0.34)
);
// Mocks
when(carRepository.findByBrand("Opel")).thenReturn(mockCarList);
when(modelMapper.map(mockCarList, new TypeToken>() {}.getType())).thenReturn(mockCarOutDtoList);
// Listado de coches aplicando el filtro por brand
List carList = carService.getAll("Opel", "");
assertEquals(1, carList.size());
assertEquals("Opel", carList.get(0).getBrand());
assertEquals("Corsa", carList.get(0).getModel());
verify(carRepository, times(0)).findAll();
verify(carRepository, times(0)).findByModel("Opel");
verify(carRepository, times(0)).findByBrandAndModel("Opel", "");
verify(carRepository, times(1)).findByBrand("Opel");
}
// TODO testGetAllByModel
// TODO testGetAllByBrandAndModel>
. . .
==== Capa Controller ====
@GetMapping("/cars")
public ResponseEntity> getAll(@RequestParam(value = "brand", defaultValue = "") String brand,
@RequestParam(value = "model", defaultValue = "") String model) {
logger.info("BEGIN getAll");
List cars = carService.getAll(brand, model);
logger.info("END getAll");
return new ResponseEntity<>(cars, HttpStatus.OK);
}
@WebMvcTest(CarController.class)
public class CarControllerTests {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private CarService carService;
@MockBean
private UserRepository userRepository;
@Test
public void testGetAllWithoutParametersReturnOk() throws Exception {
List mockCarOutDtoList = List.of(
new CarOutDto(1, "Opel", "Corsa", 1, 0.5, 0.34),
new CarOutDto(2, "Ford", "Fiesta", 1, 0.5, 0.34),
new CarOutDto(3, "Ford", "Ka", 2, 0.2, 0.56)
);
when(carService.getAll("", "")).thenReturn(mockCarOutDtoList);
MvcResult response = mockMvc.perform(MockMvcRequestBuilders.get("/cars")
.accept(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isOk())
.andReturn();
String jsonResponse = response.getResponse().getContentAsString();
List carListResponse = objectMapper.readValue(jsonResponse, new TypeReference<>(){});
assertNotNull(carListResponse);
assertEquals(3, carListResponse.size());
assertEquals("Opel", carListResponse.get(0).getBrand());
assertEquals("Corsa", carListResponse.get(0).getModel());
}
@Test
public void testGetAllByBrandReturnOk() throws Exception {
List mockCarOutDtoList = List.of(
new CarOutDto(1, "Opel", "Corsa", 1, 0.5, 0.34),
new CarOutDto(2, "Opel", "Astra", 1, 0.5, 0.34)
);
when(carService.getAll("Opel", "")).thenReturn(mockCarOutDtoList);
MvcResult response = mockMvc.perform(MockMvcRequestBuilders.get("/cars")
.queryParam("brand", "Opel")
.accept(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isOk())
.andReturn();
String jsonResponse = response.getResponse().getContentAsString();
List carListResponse = objectMapper.readValue(jsonResponse, new TypeReference<>(){});
assertNotNull(carListResponse);
assertEquals(2, carListResponse.size());
assertEquals("Opel", carListResponse.get(0).getBrand());
assertEquals("Corsa", carListResponse.get(0).getModel());
}
// TODO testGetAllByModelReturnOk
// TODO testGetAllByBrandAndModelReturnOk
. . .
===== Tests de integración =====
(c) 2025 Santiago Faci