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 ()로 몇 가지 트릭을 시도했지만 아무것도 작동하지 않았습니다.


19
조언과 마찬가지로 400 상태 코드는와 같은 것으로 반환해서는 안됩니다 "Username already taken". 그것은 409 갈등이 더 많아야합니다.
Sotirios Delimanolis

감사합니다-이 테스트의 목표는 그러한 것들을 지정하는 것입니다.
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().
Sotirios Delimanolis

5
@SotiriosDelimanolis는 정확합니다 ... 지금 주석 처리 된 컨트롤러 getContentAsString()에서 가져온 JSON을보고 @RestController있습니다.
Paul

오류 메시지에서 내가 찾던 것을 발견했습니다.result.getResponse().getErrorMessage()
whistling_marmot

andReturn ()은 null 값을 반환합니다
Giriraj

@Giriraj andReturn여기MvcResult javadoc에 지정된대로 a를 반환합니다 .
Sotirios Delimanolis

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
누군가와 같이 동적 ID를 가진 메시지가있는 경우를 대비하여 string () 메서드도 hamcrest containsString 매처를 허용한다는 것을 아는 것이 도움이됩니다 ..andExpect(content().string(containsString("\"Username already taken");
molholm

4
@ TimBüthe, 그건 틀렸어. 그러한 문제가있는 경우 분명히 예상되는 행동이 아니며 내 코드에서 목격 한 행동이 아니기 때문에 질문으로 게시해야합니다.
Paul

2
가져 오기는 org.hamcrest.Matchers.containsString()입니다.
membersound

또한 org.hamcrest.Matchers.equalToIgnoringWhiteSpace()모든 공백 문자를 무시하기 위해 matcher를 사용했습니다 . 누군가에게 유용한 팁일 것입니다
Iwo Kucharski

66

Spring MockMvc는 이제 JSON을 직접 지원합니다. 그래서 당신은 단지 말합니다 :

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

문자열 비교와 달리 "missing field xyz"또는 "message Expected 'ok'got 'nok'와 같은 메시지가 표시됩니다.

이 방법은 Spring 4.1에서 도입되었습니다.


2
당신은 완전한 예를 제공 할 수 있습니까? ContentRequestMatchers이 기능도 지원 하지 않아도 됩니까?
Zarathustra

49

이 답변을 읽으면 Spring 버전 4.x와 관련된 많은 것을 볼 수 있습니다. 여러 가지 이유로 버전 3.2.0을 사용하고 있습니다. 따라서 json과 같은 것은 바로 지원할 content()수 없습니다.

나는 사용 MockMvcResultMatchers.jsonPath이 정말 쉽고 치료법 이라는 것을 알았습니다 . 다음은 post 메소드를 테스트하는 예제입니다.

이 솔루션의 장점은 전체 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 mock 데이터 파일에서 쉽게로드 할 수 있지만 질문에서 벗어난 것처럼 여기에 포함시키지 않았습니다.

반환 된 실제 json은 다음과 같습니다.

{
    "data":"some value"
}

".andExpect (MockMvcResultMatchers.jsonPath ("$. data "). value (expectedData))"에 대한
kudos

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))
이안 본입

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

23

스프링 시큐리티 @WithMockUser와 hamcrest의 containsStringmatcher는 간단하고 우아한 솔루션을 만듭니다.

@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 형식의 Bean으로 요청을 보내는 방법에 대한 예입니다.

  @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이며 UpdateBookResponseJSON에서 구문 분석 된 응답 객체입니다. Jakson의 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!");
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.