Help us understand the problem. What is going on with this article?

MockMvcでAssertJを使用しSpring MVCで作成したREST APIのレスポンスJSONをテストする

やりたいこと

 Spring MVCの@RestControllerを使用し作成したREST APIのレスポンスをMockMVC使用してテストする際に、AssertJを使用してレスポンスJSONをテストする。

MockMVCとは

 Springが提供するサーバを起動せずにコントローラへのHTTPリクエストとレスポンスをテストするための仕組み。テスト実行時にサーバを起動しないためテストの実行速度が速くなるメリットがある。@AutoConfigureMockMvcを使用することでSpring application contextを起動できる、これによりアプリケーション全体を起動した場合と同じようにDIを行うことも可能となる。

参考サイト:
Testing the Web Layer

AssertJを使用する理由

 MockMVC+@AutoConfigureMockMvcを用いたテストは高速にアプリケーション全体をテストできるというメリットがあるが、
RestControllerから返却されるJSONをチェックするための仕組みが弱いと感じた。レスポンスのJSONを期待結果と比較するためのメソッドも用意はされているが期待結果のJSONを文字列で渡さなくてはいけない。

参考サイト:
Class ContentResultMatchers

例えば、以下のようなBookリストを返すRestContorllerをテストすることを考える。
※import文とサービスクラスの実装は省略している。

レスポンス用クラス

Book.java
@Data
public class Book {

    private int id;
    private String name;
    private String author;
}

Books.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Books {

    private List<Book> books;
}

コントローラクラス

BookController
@RestController
public class BookController {

    // Bookリストを返すサービスクラス
    private final BookService bookService;

    @Autowired
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping("books")
    public Books get() {

        // Bookリストをサービスで取得して返す
        return bookService.getBooks();
    }
}

 ContentResultMatchersクラス内にはJSONをチェックするためのpublic ResultMatcher json(String jsonContent)メソッドが
用意されている。このjsonメソッドを用いる場合、引数には期待結果のJSON文字列を渡す。jsonメソッド内部では渡された文字列をJSONオブジェクトに変換後、テスト対象のプログラムのレスポンスと比較される。

BookControllerTest
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test() throws Exception {

        String expectedJson = "{books:[...]}";  // 期待結果のJSONを文字列で作成

        mockMvc.perform(get("/books"))
                .andExpect(status().isOk()) // レスポンスのステータスコードをチェック
                .andExpect(content().json(expectedJson)); // レスポンスのJSONをチェック
    }
}

jsonメソッドでテストを書いてみたが以下のように考えた。

・JSONの項目が多い場合は期待結果のJSON文字列を作成するのは大変である。また、文字列ではIDEの補完が効かないためタイポが発生しやすい。

・Javaオブジェクトを作成しJacksonで文字列へ変換した方がミスが少なそう。

・Javaオブジェクトを作成するのであれば、MockMVCから返却される値をJavaオブジェクトへ変換して比較した方が効率的ではないか?オブジェクト同士の比較であればAssertJを使用した方がマッチャが充実していて便利ではないか。

MockMVCの返り値をオブジェクトへ変換しAssertJで比較す手順

 MockMVCではレスポンスボディを文字列で取得できるメソッドが用意されているので、これを利用する。取得した文字列をJacksonでオブジェクトへ変換しAssertJを使用し期待結果と比較する。コードは以下のようになる。
 

BookControllerTest
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test() throws Exception {

        String responseJsonString = mockMvc.perform(get("/books"))
                .andExpect(status().isOk())
                .andReturn().getResponse().getContentAsString(); // レスポンスボディを文字列として取得

        ObjectMapper objectMapper = new ObjectMapper();

        // JacksoでJavaオブジェクトへ変換
        Books responseJson = objectMapper.readValue(responseJsonString, Books.class);

        // 期待結果をJavaオブジェクトで作成
        Books expected = new Books(new ArrayList<>());

        // 比較
        assertThat(responseJson).isEqualTo(expected);

    }
}

最後に

もともと用意されているjsonメソッドを用いた方がよいかは、対象APIが返すJSONの内容にもよるし、好みもあると思う。
このような方法もあるという参考になればよいと思う。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした