Як перевірити рядок у тілі відповіді з mockMvc


243

У мене простий інтеграційний тест

@Test
public void shouldReturnErrorMessageToAdminWhenCreatingUserWithUsedUserName() throws Exception {
    mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
        .content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
        .andDo(print())
        .andExpect(status().isBadRequest())
        .andExpect(?);
}

В останньому рядку я хочу порівняти рядок, отриману в тілі відповіді, з очікуваною рядком

І у відповідь я отримую:

MockHttpServletResponse:
          Status = 400
   Error message = null
         Headers = {Content-Type=[application/json]}
    Content type = application/json
            Body = "Username already taken"
   Forwarded URL = null
  Redirected URL = null

Спробував кілька хитрощів із вмістом (), body (), але нічого не вийшло.


19
Як і порада, 400 код статусу не слід повертати для чогось подібного "Username already taken". Це повинно бути більше конфлікту 409.
Сотіріос Деліманоліс

Дякую - мета цього тесту - конкретизувати такі речі.
pbaranski

Відповіді:


356

Ви можете зателефонувати andReturn()та використати повернутий MvcResultоб'єкт, щоб отримати вміст як String.

Дивись нижче:

MvcResult result = mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
            .content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
            .andDo(MockMvcResultHandlers.print())
            .andExpect(status().isBadRequest())
            .andReturn();

String content = result.getResponse().getContentAsString();
// do what you will 

7
@ TimBüthe Ви можете уточнити? A @RestControllerвказує на те, що всі оброблювальні методи неявно зазначаються @ResponseBody. Це означає, що Spring використовуватиме a, HttpMessageConverterщоб серіалізувати повернене значення обробника і записати його у відповідь. Ти дуже можеш здобути тіло content().
Сотіріос Деліманоліс

5
@SotiriosDelimanolis є правильним ... Я зараз дивлюсь на JSON, getContentAsString()який повернувся з моїм @RestController-анотаційним контролером.
Павло

Я дізнався, що шукав у повідомленні про помилку:result.getResponse().getErrorMessage()
whistling_marmot

andReturn () повертає нульове значення
Giriraj

@Giriraj andReturnповертають MvcResult, як зазначено в Javadoc тут .
Сотіріос Деліманоліс

105

@Sotirios Delimanolis відповідь виконувати роботу, але я шукав порівняння рядків в цьому тверді mockMvc

Так ось воно

.andExpect(content().string("\"Username already taken - please try with different username\""));

Звичайно, моє твердження провалюється:

java.lang.AssertionError: Response content expected:
<"Username already taken - please try with different username"> but was:<"Something gone wrong">

тому що:

  MockHttpServletResponse:
            Body = "Something gone wrong"

Тож це доказ того, що це працює!


17
На всякий випадок, якщо у когось є повідомлення з динамічними ідентифікаторами, як у мене, корисно знати, що метод string () також приймає hamcrest містить Matttcher :.andExpect(content().string(containsString("\"Username already taken");
molholm,

4
@ TimBüthe, це неправильно. Якщо у вас є така проблема, слід поставити її як питання, оскільки це, безумовно, не очікувана поведінка, а не поведінка, якою я був свідком власного коду.
Пол

2
Тільки зауважте, що імпорт є org.hamcrest.Matchers.containsString().
membersound

Я також використовував org.hamcrest.Matchers.equalToIgnoringWhiteSpace()matcher, щоб ігнорувати всі символи пробілу. Можливо, це стане в нагоді для когось
Iwo Kucharski

66

Spring MockMvc тепер має пряму підтримку JSON. Отже, ви просто говорите:

.andExpect(content().json("{'message':'ok'}"));

і на відміну від порівняння рядків, воно скаже щось на кшталт "відсутні поле xyz" або "повідомлення очікувано" нормально "отримано" nok ".

Цей метод був запроваджений навесні 4.1.


2
ви могли б надати повний приклад? Вам також не потрібно ContentRequestMatchersпідтримувати цю функцію?
Заратустра

49

Читаючи ці відповіді, я бачу багато що стосується весняної версії 4.x, я використовую версію 3.2.0 з різних причин. Тож такі речі, як json, підтримувати прямо з боку content()неможливо.

Я виявив, що користуватися MockMvcResultMatchers.jsonPathсправді просто і працює як задоволення. Ось приклад тестування методу публікації.

Бонус за допомогою цього рішення полягає в тому, що ви все ще збігаєтеся за атрибутами, не покладаючись на повні порівняння рядків json.

(Використання org.springframework.test.web.servlet.result.MockMvcResultMatchers)

String expectedData = "some value";
mockMvc.perform(post("/endPoint")
                .contentType(MediaType.APPLICATION_JSON)
                .content(mockRequestBodyAsString.getBytes()))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.data").value(expectedData));

Тіло запиту було лише рядком json, який ви можете легко завантажити з реального файлу даних json макету, якщо хочете, але я не включив це сюди, як це би відхилилося від питання.

Фактичний повернутий json виглядав би так:

{
    "data":"some value"
}

kudos for ".andExpect (MockMvcResultMatchers.jsonPath (" $. data "). значення (очікувані дані))"
користувач1697575

28

Взято з весняного підручника

mockMvc.perform(get("/" + userName + "/bookmarks/" 
    + this.bookmarkList.get(0).getId()))
    .andExpect(status().isOk())
    .andExpect(content().contentType(contentType))
    .andExpect(jsonPath("$.id", is(this.bookmarkList.get(0).getId().intValue())))
    .andExpect(jsonPath("$.uri", is("http://bookmark.com/1/" + userName)))
    .andExpect(jsonPath("$.description", is("A description")));

is доступний від import static org.hamcrest.Matchers.*;

jsonPath доступний від import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

та jsonPathдовідку можна знайти тут


1
Я error: incompatible types: RequestMatcher cannot be converted to ResultMatcher за.andExpect(content().contentType(contentType))
Ian Vaughan

@IanVaughan MockMvcResultMatchers.content (). ContentType (contentType)
Rajkumar

23

@WithMockUserМатч безпеки та весняної безпеки забезпечує containsStringпросте і елегантне рішення:

@Test
@WithMockUser(roles = "USER")
public void loginWithRoleUserThenExpectUserSpecificContent() throws Exception {
    mockMvc.perform(get("/index"))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("This content is only shown to users.")));
}

Більше прикладів на github


4

Ось приклад, як проаналізувати відповідь JSON і навіть як надіслати запит з бобом у формі JSON:

  @Autowired
  protected MockMvc mvc;

  private static final ObjectMapper MAPPER = new ObjectMapper()
    .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    .registerModule(new JavaTimeModule());

  public static String requestBody(Object request) {
    try {
      return MAPPER.writeValueAsString(request);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }

  public static <T> T parseResponse(MvcResult result, Class<T> responseClass) {
    try {
      String contentAsString = result.getResponse().getContentAsString();
      return MAPPER.readValue(contentAsString, responseClass);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  @Test
  public void testUpdate() {
    Book book = new Book();
    book.setTitle("1984");
    book.setAuthor("Orwell");
    MvcResult requestResult = mvc.perform(post("http://example.com/book/")
      .contentType(MediaType.APPLICATION_JSON)
      .content(requestBody(book)))
      .andExpect(status().isOk())
      .andReturn();
    UpdateBookResponse updateBookResponse = parseResponse(requestResult, UpdateBookResponse.class);
    assertEquals("1984", updateBookResponse.getTitle());
    assertEquals("Orwell", updateBookResponse.getAuthor());
  }

Як ви бачите тут Book, це DTO запиту, і UpdateBookResponseце об'єкт відповіді, проаналізований від JSON. Ви можете змінити ObjectMapperконфігурацію Джексона .


2
String body = mockMvc.perform(bla... bla).andReturn().getResolvedException().getMessage()

Це має дати вам відповідь. У вашому випадку "Ім'я користувача вже взято".


де пояснення? вона потрібна або ви можете дати їй у коментарі такий тип відповіді
user1140237

2

тут більш елегантний спосіб

mockMvc.perform(post("/retrieve?page=1&countReg=999999")
            .header("Authorization", "Bearer " + validToken))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("regCount")));

2

Ви можете скористатися методом 'getContentAsString', щоб отримати дані відповіді як рядкові.

    String payload = "....";
    String apiToTest = "....";

    MvcResult mvcResult = mockMvc.
                perform(post(apiToTest).
                content(payload).
                contentType(MediaType.APPLICATION_JSON)).
                andReturn();

    String responseData = mvcResult.getResponse().getContentAsString();

Ви можете посилатися на це посилання для тестової програми.


1

Один з можливих підходів - просто включити gsonзалежність:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>

і проаналізуйте значення, щоб зробити перевірки:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private HelloService helloService;

    @Before
    public void before() {
        Mockito.when(helloService.message()).thenReturn("hello world!");
    }

    @Test
    public void testMessage() throws Exception {
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/"))
                .andExpect(status().isOk())
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE))
                .andReturn();

        String responseBody = mvcResult.getResponse().getContentAsString();
        HelloController.ResponseDto responseDto
                = new Gson().fromJson(responseBody, HelloController.ResponseDto.class);
        Assertions.assertThat(responseDto.message).isEqualTo("hello world!");
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.