Как проверить строку в теле ответа с помощью 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

Пробовал некоторые приемы с content (), body (), но ничего не получалось.

pbaranski
источник
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 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"

Так что это доказательство того, что это работает!

pbaranski
источник
17
На всякий случай, если у кого-то есть сообщения с динамическими идентификаторами, как и у меня, полезно знать, что метод string () также принимает Hamcrest containsString matcher :.andExpect(content().string(containsString("\"Username already taken");
molholm
4
@ TimBüthe, это неправильно. Если у вас возникла такая проблема, вы должны опубликовать ее как вопрос, потому что это определенно не ожидаемое поведение и поведение, которое я видел в своем собственном коде.
Пол
2
Просто отметьте, что импорт есть org.hamcrest.Matchers.containsString().
membersound
Я также использовал org.hamcrest.Matchers.equalToIgnoringWhiteSpace()matcher, чтобы игнорировать все символы пробела. Может быть, это будет полезный совет для кого-то
Иво Кучарски
66

Spring MockMvc теперь имеет прямую поддержку JSON. Итак, вы просто говорите:

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

и в отличие от сравнения строк, он будет говорить что-то вроде «пропущенное поле xyz» или «сообщение Ожидается, что« ок »получил« nok ».

Этот метод был представлен весной 4.1.

vertti
источник
2
Не могли бы вы привести полный пример? Не нужно ContentRequestMatchersли также поддерживать эту функцию?
Заратустра
49

Читая эти ответы, я вижу многое, касающееся Spring версии 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"
}
Джереми
источник
слава за ".andExpect (MockMvcResultMatchers.jsonPath (" $. data "). value (Ожидаемые данные))"
user1697575
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ссылку можно найти здесь

user2829759
источник
1
Я получаю error: incompatible types: RequestMatcher cannot be converted to ResultMatcher за.andExpect(content().contentType(contentType))
Ян Vaughan
@IanVaughan MockMvcResultMatchers.content (). ContentType (contentType)
Раджкумар
23

Spring Security @WithMockUserи Hamcrest 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 и даже как отправить запрос с bean-компонентом в форме 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конфигурацию Jakson .

Сергей Пономарев
источник
2
String body = mockMvc.perform(bla... bla).andReturn().getResolvedException().getMessage()

Это должно дать вам тело ответа. «Имя пользователя уже занято» в вашем случае.

justAnotherGuy
источник
где объяснение? это нужно или вы можете дать в комментариях такой ответ
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!");
    }
}
Корай Тугай
источник