Spring MockMvcのREST保証されたサポート

1. 前書き

このチュートリアルでは、Springの_MockMvc_の上に構築されたREST保証APIである_RestAssuredMockMvc_ *を使用してSpring RESTコントローラーをテストする方法を学習します。
最初に、さまざまなセットアップオプションを調べます。 次に、単体テストと統合テストの両方を作成する方法について説明します。
このチュートリアルでは、https://www.baeldung.com/spring-mvc [Spring MVC]、https://www.baeldung.com/integration-testing-test-in-spring [Spring MockMVC]、およびhttps:// wwwを使用します。 baeldung.com/rest-assured-tutorial[REST-assured]なので、これらのチュートリアルも必ずチェックしてください。

2. メーベン依存

テストの記述を開始する前に、https://search.maven.org/search?q = g:io.rest-assured%20AND%20a:spring-mock-mvc [_io.restをインポートする必要があります。 -assured:spring-mock-mvc_ module]をMaven _pom.xml_に追加します。
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>spring-mock-mvc</artifactId>
    <version>3.3.0</version>
    <scope>test</scope>
</dependency>

3. _RestAssuredMockMvc_の初期化

次に、_standalone_または_web application context_モードで、DSLの開始点である_RestAssuredMockMvc、_を初期化する必要があります。
両方のモードで、テストごとにジャストインタイムで実行するか、静的に1回実行できます。 いくつかの例を見てみましょう。

3.1. スタンドアロン

スタンドアロンモードでは、1つ以上の_https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Controller.html [@Controller] _または_httpsで_RestAssuredMockMvc_を*初期化します://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html [@ControllerAdvice] _注釈付きクラス。*
いくつかのテストしかない場合は、_RestAssuredMockMvc_をすぐに初期化できます。
@Test
public void whenGetCourse() {
    given()
      .standaloneSetup(new CourseController())
      //...
}
ただし、多くのテストがある場合は、静的に1回実行するだけで簡単になります。
@Before
public void initialiseRestAssuredMockMvcStandalone() {
    RestAssuredMockMvc.standaloneSetup(new CourseController());
}

3.2. Webアプリケーションコンテキスト

Webアプリケーションコンテキストモードでは、Spring https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/WebApplicationContext.htmlのインスタンスで_RestAssuredMockMvc_を*初期化します[ _WebApplicationContext _]。*
スタンドアロンモードのセットアップで見たものと同様に、各テストのタイミングで_RestAssuredMockMvc_を初期化できます。
@Autowired
private WebApplicationContext webApplicationContext;

@Test
public void whenGetCourse() {
    given()
      .webAppContextSetup(webApplicationContext)
      //...
}
または、もう一度、静的に1回だけ実行できます。
@Autowired
private WebApplicationContext webApplicationContext;

@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
    RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}

4. テスト対象システム(SUT)

いくつかのサンプルテストに飛び込む前に、テストするものが必要になります。 _https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/SpringBootApplication.html [@SpringBootApplication] _構成から始めて、テスト対象のシステムをチェックしてみましょう。
@SpringBootApplication
class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
次に、簡単な_https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RestController.html [@RestController] _が_Course_を公開していますドメイン:
@RestController
@RequestMapping(path = "/courses")
public class CourseController {

    private final CourseService courseService;

    public CourseController(CourseService courseService) {
        this.courseService = courseService;
    }

    @GetMapping(produces = APPLICATION_JSON_UTF8_VALUE)
    public Collection<Course> getCourses() {
        return courseService.getCourses();
    }

    @GetMapping(path = "/{code}", produces = APPLICATION_JSON_UTF8_VALUE)
    public Course getCourse(@PathVariable String code) {
        return courseService.getCourse(code);
    }
}
class Course {

    private String code;

    // usual contructors, getters and setters
}
そして最後になりましたが、サービスクラスと_CourseNotFoundException_を処理する_ @ ControllerAdvice_:
@Service
class CourseService {

    private static final Map<String, Course> COURSE_MAP = new ConcurrentHashMap<>();

    static {
        Course wizardry = new Course("Wizardry");
        COURSE_MAP.put(wizardry.getCode(), wizardry);
    }

    Collection<Course> getCourses() {
        return COURSE_MAP.values();
    }

    Course getCourse(String code) {
        return Optional.ofNullable(COURSE_MAP.get(code)).orElseThrow(() ->
          new CourseNotFoundException(code));
    }
}
@ControllerAdvice(assignableTypes = CourseController.class)
public class CourseControllerExceptionHandler extends ResponseEntityExceptionHandler {

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(CourseNotFoundException.class)
    public void handleCourseNotFoundException(CourseNotFoundException cnfe) {
        //...
    }
}
class CourseNotFoundException extends RuntimeException {

    CourseNotFoundException(String code) {
        super(code);
    }
}
テストするシステムができたので、いくつかの_RestAssuredMockMvc_テストを見てみましょう。

5. RESTを使用したRESTコントローラーユニットテスト

_RestAssuredMockMvc_をお気に入りのテストツールlink:/junit[JUnit]およびlink:/mockito-series[Mockito]で使用して、_ @ RestController_をテストできます。
まず、SUTをモックして構築し、上記のように_RestAssuredMockMvc_をスタンドアロンモードで初期化します。
@RunWith(MockitoJUnitRunner.class)
public class CourseControllerUnitTest {

    @Mock
    private CourseService courseService;
    @InjectMocks
    private CourseController courseController;
    @InjectMocks
    private CourseControllerExceptionHandler courseControllerExceptionHandler;

    @Before
    public void initialiseRestAssuredMockMvcStandalone() {
        RestAssuredMockMvc.standaloneSetup(courseController, courseControllerExceptionHandler);
    }
_ @ Before_メソッドで_RestAssuredMockMvc_を静的に初期化したため、各テストで初期化する必要はありません。
*スタンドアロンモードは、アプリケーションコンテキスト全体ではなく、提供するコントローラーのみを初期化するため、単体テストに最適です。 これは、我々のテストは、高速続けます。
次に、テストの例を見てみましょう。
@Test
public void givenNoExistingCoursesWhenGetCoursesThenRespondWithStatusOkAndEmptyArray() {
    when(courseService.getCourses()).thenReturn(Collections.emptyList());

    given()
      .when()
        .get("/courses")
      .then()
        .log().ifValidationFails()
        .statusCode(OK.value())
        .contentType(JSON)
        .body(is(equalTo("[]")));
}
_ @ RestController_に加えて_ @ ControllerAdvice_で_RestAssuredMockMvc_を初期化すると、例外シナリオもテストできます。
@Test
public void givenNoMatchingCoursesWhenGetCoursesThenRespondWithStatusNotFound() {
    String nonMatchingCourseCode = "nonMatchingCourseCode";

    when(courseService.getCourse(nonMatchingCourseCode)).thenThrow(
      new CourseNotFoundException(nonMatchingCourseCode));

    given()
      .when()
        .get("/courses/" + nonMatchingCourseCode)
      .then()
        .log().ifValidationFails()
        .statusCode(NOT_FOUND.value());
}
上記のように、REST保証では、使い慣れた所定のシナリオ形式を使用してテストを定義します。
  • given() – HTTPリクエストの詳細を指定します

  • when() — HTTP動詞とルートを指定します

  • then() — HTTP応答を検証します

6. REST保証付きRESTコントローラー統合テスト

統合テスト用にSpringのテストツールで_RestAssuredMockMvc_を使用することもできます。
 最初に、_ @ RunWith(SpringRunner.class)_およびhttps://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTestを使用してテストクラスを設定します。 .html [_ @ SpringBootTest(webEnvironment
=  RANDOM_PORT)_]:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
public class CourseControllerIntegrationTest {
    //...
}
これにより、ランダムポート上の_ @ SpringBootApplication_クラスで構成されたアプリケーションコンテキストでテストが実行されます。
次に、_WebApplicationContext_を挿入し、それを使用して上記のように_RestAssuredMockMvc_を初期化します。
@Autowired
private WebApplicationContext webApplicationContext;

@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
    RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
テストクラスをセットアップし、_RestAssuredMockMvc_を初期化したので、テストの作成を開始する準備ができました。
@Test
public void givenNoMatchingCourseCodeWhenGetCourseThenRespondWithStatusNotFound() {
    String nonMatchingCourseCode = "nonMatchingCourseCode";

    given()
      .when()
        .get("/courses/" + nonMatchingCourseCode)
      .then()
        .log().ifValidationFails()
        .statusCode(NOT_FOUND.value());
}
_ @ Before_メソッドで_RestAssuredMockMvc_を静的に初期化したため、各テストで初期化する必要はありません。
REST保証APIの詳細については、https://www.baeldung.com/rest-assured-tutorial [REST-assured Guide]をご覧ください。

7. 結論

このチュートリアルでは、REST保証の_spring-mock-mvc_モジュールを使用して、REST保証を使用してSpring MVCアプリケーションをテストする方法を説明しました。
_RestAssuredMockMvc_を* standaloneモードで初期化することは、提供された__Controller__sを初期化するだけなので、テストを高速に保つため、単体テストに最適です。
_RestAssuredMockMvc_を* Webアプリケーションコンテキストモードで初期化することは、完全な_WebApplicationContext_を使用するため、統合テストに最適です。
いつものように、すべてのサンプルコードhttps://github.com/eugenp/tutorials/tree/master/testing-modules/rest-assured[Github上]を見つけることができます。