Рубрики
Без рубрики

Гарантированная поддержка Spring MockMvc

Узнайте, как протестировать контроллеры Spring REST с помощью API RestAssuredMockMvc от REST-assured.

Автор оригинала: baeldung.

1. введение

В этом уроке мы узнаем, как протестировать наши контроллеры Spring REST с помощью RestAssuredMockMvc , API-интерфейса REST, построенного поверх/| MockMvc Spring .

Во-первых, мы рассмотрим различные варианты настройки. Затем мы погрузимся в то, как писать как модульные, так и интеграционные тесты.

В этом учебнике используются Spring MVC , Spring MockMvc и REST-assured , поэтому обязательно ознакомьтесь с этими учебниками.

2. Зависимость Maven

Прежде чем мы начнем писать наши тесты, нам нужно будет импортировать io.rest-assured:spring-mock-mvc модуль в ваш Maven pom.xml :


    io.rest-assured
    spring-mock-mvc
    3.3.0
    test

3. Инициализация RestAssuredMockMvc

Далее нам нужно инициализировать RestAssuredMockMvc, начальную точку DSL, в режиме автономный или контекст веб-приложения|/.

В обоих режимах мы можем сделать это либо точно в срок за тест, либо один раз статически. Давайте рассмотрим несколько примеров.

3.1. Автономный

В автономном режиме мы инициализируем RestAssuredMockMvc с помощью одного или нескольких @Controller или @ControllerAdvice аннотированных классов.

Если у нас есть только несколько тестов, мы можем инициализировать RestAssuredMockMvc как раз вовремя:

@Test
public void whenGetCourse() {
    given()
      .standaloneSetup(new CourseController())
      //...
}

Но, если у нас будет много тестов, будет проще сделать это один раз статически:

@Before
public void initialiseRestAssuredMockMvcStandalone() {
    RestAssuredMockMvc.standaloneSetup(new CourseController());
}

3.2. Контекст веб-приложения

В модели контекста веб-приложения мы инициализируем RestAssuredMockMvc с помощью экземпляра Spring WebApplicationContext .

Аналогично тому, что мы видели в настройке автономного режима, мы можем инициализировать RestAssuredMockMvc точно во время каждого теста:

@Autowired
private WebApplicationContext webApplicationContext;

@Test
public void whenGetCourse() {
    given()
      .webAppContextSetup(webApplicationContext)
      //...
}

Или, опять же, мы можем просто сделать это один раз статически:

@Autowired
private WebApplicationContext webApplicationContext;

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

4. Тестируемая система (SUT)

Прежде чем мы погрузимся в несколько примеров тестов, нам нужно будет что-то проверить. Давайте проверим нашу тестируемую систему, начиная с нашей @SpringBootApplication конфигурации:

@SpringBootApplication
class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Далее, у нас есть простой @RestController , раскрывающий наш Курс домен:

@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 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
}

И, последнее, но не менее важное, наш класс обслуживания и @ControllerAdvice для обработки нашего курса NotFoundException :

@Service
class CourseService {

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

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

    Collection 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. Тестирование блока контроллера покоя с гарантией ПОКОЯ

Мы можем использовать RestAssuredMockMvc с нашими любимыми инструментами тестирования, JUnit и Mockito , для тестирования нашего @RestController .

Сначала мы издеваемся и создаем наш КОСТЮМ, а затем инициализируем 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);
    }

Поскольку мы инициализировали RestAssuredMockMvc статически в нашем методе @Before , нет необходимости инициализировать его в каждом тесте.

Автономный режим отлично подходит для модульных тестов , поскольку он инициализирует только контроллеры, которые мы предоставляем , а не весь контекст приложения. Это ускоряет наши тесты.

Теперь давайте рассмотрим пример теста:

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

Инициализация RestAssuredMockMvc с помощью нашего @ControllerAdvice в дополнение к нашему @RestController позволяет нам также тестировать наши сценарии исключений:

@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-assured использует знакомый формат сценария “задано-когда-тогда” для определения теста:

  • given() — указывает детали HTTP-запроса
  • when() — указывает глагол HTTP, а также маршрут
  • затем() — проверяет HTTP-ответ

6. Тестирование интеграции контроллера REST с гарантированным отдыхом

Мы также можем использовать RestAssuredMockMvc с инструментами тестирования Spring для наших интеграционных тестов.

Во-первых, мы настроим ваш тестовый класс с помощью @RunWith(SpringRunner.class) и @SpringBootTest(веб-среда) :

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

Помните, что, поскольку мы инициализировали RestAssuredMockMvc статически в нашем методе @Before , нет необходимости инициализировать его в каждом тесте.

Для более глубокого погружения в API REST-assured ознакомьтесь с нашим руководством REST-assured .

7. Заключение

В этом уроке мы увидели, как мы можем использовать REST-assured для тестирования вашего приложения Spring MVC с помощью модуля REST-assured spring-mock-mvc .

Инициализация RestAssuredMockMvc в автономном режиме отлично подходит для модульного тестирования , так как он инициализирует только предоставленный Контроллер s, сохраняя наши тесты быстрыми.

Инициализация RestAssuredMockMvc в модели контекста веб-приложения отлично подходит для интеграционного тестирования , поскольку она использует наш полный WebApplicationContext .

Как всегда, вы можете найти весь наш пример кода на Github .