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 CollectiongetCourses() { 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 MapCOURSE_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 .