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 2013

Thanx-このテストの目的は、そのようなものを指定することです。
pbaranski 2013

回答:


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はを使用しHttpMessageConverterてハンドラーの戻り値をシリアル化し、応答に書き込みます。あなたは非常に体を得ることができcontent()ます。
Sotirios Delimanolis 2014年

5
@SotiriosDelimanolisは正解です...私は今getContentAsString()@RestController注釈付きコントローラーから返されたJSONを返しています。
Paul

私は、エラーメッセージに探していたものが見つかりました:result.getResponse().getErrorMessage()
whistling_marmot

andReturn()がnull値を返す
Giriraj

@Giriraj andReturnMvcResultこちらのjavadoc 指定されているを返します
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を持つメッセージを持って、その文字列()メソッドもhamcrest受け入れるcontainsStringのマッチャー:.andExpect(content().string(containsString("\"Username already taken");
molholm

4
@TimBüthe、それは間違っています。このような問題が発生した場合は、それを質問として投稿してください。これは、予想される動作ではなく、自分のコードで確認した動作でもないためです。
Paul

2
インポートはであることに注意してくださいorg.hamcrest.Matchers.containsString()
メンバーサウンド

また、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、2015

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モックデータファイルから簡単にロードできますが、質問から逸脱しているため、ここには含めません。

返される実際のjsonは次のようになります。

{
    "data":"some value"
}

".andExpect(MockMvcResultMatchers.jsonPath(" $。data ")
。value

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
I get error: incompatible types: RequestMatcher cannot be converted to ResultMatcher for.andExpect(content().contentType(contentType))
イアンヴォーン

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

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応答を解析する方法の例と、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

考えられるアプローチの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.