Модульный тест MockHttpServletRequest не возвращает тип контента - PullRequest
2 голосов
/ 27 апреля 2020

Я бы хотел, чтобы приложение возвращало JSON объект из моих Java Классов (как успешных, так и неудачных).

Я определил @RestControllerAdvice для обработки ошибок от контроллера. Моя программа также правильно отображает сообщение об ошибке в json, но проблема в модульном тесте .

Проблема в том, что выдает:

org.springframework.web.bind.MethodArgumentNotValidException

Мой модульный тест не пройден с ошибкой:

java.lang.AssertionError: Response header 'content-type' expected:<application/json;charset=UTF-8> but was:<null>

Контроллер:

@PostMapping("/import")
public ResponseEntity<StatusModel> import(@Valid @RequestBody ImportModel importModel ){
    //logic
    return new ResponseEntity<>(new StatusModel("Data accepted."), HttpStatus.OK);

}

Модульный тест :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MockConfiguration.class})
@WebAppConfiguration
public class ModelControllerTest {

    private MockMvc mockMvc;

    @InjectMocks
    private ModelController controller;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void import_validRequest_imported() throws Exception {

        mockMvc
            .perform(
                post("/import")
                    .content(VALID_CONTENT).contentType("application/json;charset=UTF-8"))
            .andExpect(status().isOk())
            .andExpect(header().string("content-type", "application/json;charset=UTF-8"))
            .andExpect(jsonPath("$.status", equalTo("Data accepted")));
    }

    @Test
    public void import_invalidRequest_notImported() throws Exception {    
        mockMvc
            .perform(
                post("/import")
                    .content(INVALID_CONTENT).contentType("application/json"))
            .andExpect(status().isBadRequest())
            .andDo(print())
            .andExpect(header().string("content-type", "application/json"));  <----- This assertion failed
    }   
}

Журнал MockHttpServletRequest:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /import
       Parameters = {}
          Headers = {Content-Type=[application/json]}

Handler:
             Type = com.test.ModelController
           Method = public org.springframework.http.ResponseEntity<com.model.StatusModel> com.ModelController.import(com.test.model.ImportModel)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = org.springframework.web.bind.MethodArgumentNotValidException

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 400
    Error message = null
          Headers = {}
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

Почему тип содержимого, сообщение об ошибке пустое?

1 Ответ

1 голос
/ 30 апреля 2020

Вот рациональное объяснение, почему mock mvc не поддерживает обработчики исключений при весенней загрузке с последующей рекомендацией и исправлением.

Рациональный отрывок

Обработка ошибок в Spring Boot основана на Отображения ошибок контейнера сервлета, которые приводят к отправке ОШИБКИ в ErrorController. Mock Mvc, однако, является тестом без контейнера, поэтому без контейнера Servlet исключение просто вспыхивает, и ничто не останавливает его.

Так что тестов Mock Mvc просто недостаточно для тестирования ответов об ошибках, генерируемых Spring Загрузочный. Я бы сказал, что вам не следует тестировать обработку ошибок Spring Boot. Если вы настраиваете его каким-либо образом, вы можете написать интеграционные тесты Spring Boot (с реальным контейнером) для проверки ответов об ошибках. А затем для тестов Mock Mvc основное внимание уделяется полному тестированию веб-слоя с ожиданием всплытия исключений.

Это типичный компромисс между тестами интеграции. Вы проводите модульные тесты, даже если они не все тестируют, потому что они дают вам больше контроля и работают быстрее.

Отрывок рекомендации

Как мы можем написать тесты для условий ошибки используя стандартные весенние ответы JSON, тогда?

@ xak2000 Россен уже рассмотрел это, но я хотел дать вам прямой ответ. Если вы действительно хотите проверить точный формат ответа об ошибке, вы можете использовать интеграционный тест, используя @SpringBootTest, настроенный с помощью веб-среды DEFINED_PORT или RANDOM_PORT и TestRestTemplate.

Полная информация здесь https://github.com/spring-projects/spring-boot/issues/7321

Fix

Здесь немного другая проверка ошибок с использованием теста Spring Boot.

import org.json.JSONException;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest(classes = DemoApplication.class,
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ModelControllerTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void import_invalidRequest_notImported() throws JSONException {

        String expected = "{\"status\":400,\"error\":\"Bad Request\",\"message\":\"JSON parse error: Unrecognized token 'Invalid': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Invalid': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\\n at [Source: (PushbackInputStream); line: 1, column: 8]\",\"path\":\"/import\"}";

        String invalidJson = "Invalid";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<>(invalidJson, headers);

        ResponseEntity<String> response = restTemplate.exchange("/import", HttpMethod.POST, entity, String.class);

        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
        assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType());
        JSONAssert.assertEquals(expected, response.getBody(), false);

    }

}

Ссылка здесь https://mkyong.com/spring-boot/spring-rest-integration-test-example/

...