【Spring】MockMvc + AssertJでREST APIのテスト

MockMvc + AssertJでREST APIテスト

この記事ではSpring MVCの@RestControllerを使用し作成したREST APIのレスポンスをMockMVC使用してテストする際に、AssertJを使用してAPIのレスポンスをチェックする方法を解説します。

MockMVCとは

MockMVCはSpringが提供するサーバを起動せずにコントローラへのHTTPリクエストとレスポンスをテストするための仕組みです。

テスト実行時にサーバを起動しないためテストの実行速度が速くなるメリットがあります。@AutoConfigureMockMvcを使用することでSpring application contextを起動できます。これによりアプリケーション全体を起動した場合と同じようにDIを行うことも可能となります。

参考サイト:Testing the Web Layer

AssertJを使用する理由

MockMVC+@AutoConfigureMockMvcを用いたテストは高速にアプリケーション全体をテストできるというメリットがあります。

しかし、RestControllerから返却されるJSONをチェックするための仕組みが弱いと感じました。レスポンスのJSONを期待結果と比較するためのメソッドも用意はされているのですが、期待結果のJSONを文字列で渡さなくてはいけないため使いやすさがイマイチな気がします。

参考サイト: Class ContentResultMatchers

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

レスポンスクラス

@Data
public class Book {

    private int id;
    private String name;
    private String author;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Books {

    private List<Book> books;
}

コントローラクラス

@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();
    }
}

テストコード

@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をチェック
    }
}

この例のようにAPIの機体結果のJSONの文字列を記述するのはIDEの補完も効かないためミスが発生しやすいです。そのため、MockMVCのレスポンスをオブジェクトへ変換しAssertJを使って機体結果と比較します。

AssertJを使ったテスト

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

コードは以下のようになります。

@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);

    }
}