У меня есть следующий метод в классе контроллера:
@PostMapping("employees")
@ResponseStatus(HttpStatus.CREATED)
public Employee addEmployee(@Valid @RequestBody Employee employee) {
try {
return employeeRepository.save(employee);
} catch (DataIntegrityViolationException e) {
e.printStackTrace();
Optional<Employee> existingEmployee = employeeRepository.findByTagId(employee.getTagId());
if (!existingEmployee.isPresent()) {
//The exception root cause was not due to a unique ID violation then
throw e;
}
throw new DuplicateEntryException(
"An employee named " + existingEmployee.get().getName() + " already uses RFID tagID " + existingEmployee.get().getTagId());
}
}
Где класс Employee
имеет строковое поле с именем tagId
, в котором есть примечание @NaturalId
. (Пожалуйста, не обращайте внимания на отсутствие выделенного слоя обслуживания, это небольшое и простое приложение).
Вот мой заказ DuplicateEntryException
:
@ResponseStatus(HttpStatus.CONFLICT)
public class DuplicateEntryException extends RuntimeException {
public DuplicateEntryException() {
super();
}
public DuplicateEntryException(String message) {
super(message);
}
public DuplicateEntryException(String message, Throwable cause) {
super(message, cause);
}
}
Благодаря строке @ResponseStatus(HttpStatus.CONFLICT)
, когда я вручную тестирую метод, я получаю стандартное сообщение REST с весенней загрузкой по умолчанию с полями timestamp, status, error, message и path.
Я все еще знакомлюсь с тестированием весной, и у меня есть этот тест:
@Test
public void _02addEmployee_whenDuplicateTagId_thenExceptionIsReturned() throws Exception {
Employee sampleEmployee = new Employee("tagId01", "John Doe");
System.out.println("Employees in the database: " + repository.findAll().size()); //prints 1
// @formatter:off
mvc.perform(post("/employees").contentType(MediaType.APPLICATION_JSON).content(JsonUtil.toJson(sampleEmployee)))
.andExpect(status().isConflict())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.message").value("An employee named John Doe already uses RFID tagID tagId01"));
// @formatter:on
int employeeCount = repository.findAll().size();
Assert.assertEquals(1, employeeCount);
}
Как вы можете догадаться, сначала выполняется другой тест, который называется _01addEmployee_whenValidInput_thenCreateEmployee()
, который вставляет сотрудника с тем же tagID, который используется в тесте № 2. Тест № 1 проходит, а тест № 2 - нет, потому что HTTP-ответ выглядит так:
MockHttpServletResponse:
Status = 409
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
А в консоли перед вышеупомянутым ответом я вижу это:
Resolved Exception:
Type = ai.aitia.rfid_employee.exception.DuplicateEntryException
Так что мой второй тест не пройден, потому что java.lang.AssertionError: Content type not set
.
Что вызывает другое поведение по сравнению с ручным тестированием? Почему это не возвращается?
{
"timestamp": "2019-01-03T09:47:33.371+0000",
"status": 409,
"error": "Conflict",
"message": "An employee named John Doe already uses RFID tagID tagId01",
"path": "/employees"
}
Обновление : я испытал то же самое и с другой конечной точкой REST, где контрольный пример привел к моему ResourceNotFoundException
, но фактический объект ошибки JSON не был получен объектом MockMvc.
Update2 : Вот мои аннотации на уровне класса для тестового класса:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = RfidEmployeeApplication.class)
@AutoConfigureMockMvc
@AutoConfigureTestDatabase
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@TestPropertySource(locations = "classpath:application-test.properties")