Suporte garantido por REST para Spring MockMvc
1. Introdução
Neste tutorial, vamos aprender comotest our Spring REST Controllers using RestAssuredMockMvc, uma API garantida por REST construída sobreMockMvc do Spring.
Primeiro, examinaremos as diferentes opções de configuração. Em seguida, veremos como escrever testes de unidade e integração.
Este tutorial usaSpring MVC,Spring MockMVC eREST-assured, portanto, certifique-se de verificar esses tutoriais também.
2. Dependência do Maven
Antes de começarmos a escrever nossos testes, precisaremos importario.rest-assured:spring-mock-mvc module em nosso Mavenpom.xml:
io.rest-assured
spring-mock-mvc
3.3.0
test
3. InicializandoRestAssuredMockMvc
Em seguida, precisamos inicializarRestAssuredMockMvc, o ponto de partida do DSL, no modostandalone ouweb application context.
Nos dois modos, podemos fazer isso just-in-time por teste ou uma vez estaticamente. Vamos dar uma olhada em alguns exemplos.
3.1. Estar sozinho
No modo autônomo, nósinitialize RestAssuredMockMvc with one or more @Controller or @ControllerAdvice annotated classes.
Se tivermos apenas alguns testes, podemos inicializarRestAssuredMockMvc bem a tempo:
@Test
public void whenGetCourse() {
given()
.standaloneSetup(new CourseController())
//...
}
Mas, se tivermos muitos testes, será mais fácil fazer apenas uma vez estaticamente:
@Before
public void initialiseRestAssuredMockMvcStandalone() {
RestAssuredMockMvc.standaloneSetup(new CourseController());
}
3.2. Contexto de Aplicativo da Web
No modo de contexto de aplicativo da web, nósinitialize RestAssuredMockMvc with an instance of a Spring WebApplicationContext.
Semelhante ao que vimos na configuração do modo autônomo, podemos inicializarRestAssuredMockMvc bem a tempo em cada teste:
@Autowired
private WebApplicationContext webApplicationContext;
@Test
public void whenGetCourse() {
given()
.webAppContextSetup(webApplicationContext)
//...
}
Ou, novamente, podemos fazer isso apenas uma vez estaticamente:
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
4. Sistema em teste (SUT)
Antes de mergulhar em alguns exemplos de testes, vamos precisar de algo para testar. Vamos verificar nosso sistema em teste, começando com nossa configuração@SpringBootApplication:
@SpringBootApplication
class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Em seguida, temos um@RestController simples expondo nosso domínioCourse:
@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
}
E, por último, mas não menos importante, nossa classe de serviço e@ControllerAdvice para lidar com nossoCourseNotFoundException:
@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);
}
}
Agora que temos um sistema para testar, vamos dar uma olhada em alguns testesRestAssuredMockMvc.
5. Teste de unidade do controlador REST com garantia REST
Podemos usarRestAssuredMockMvc com nossas ferramentas de teste favoritas,JUniteMockito, para testar nosso@RestController.
Primeiro, simulamos e construímos nosso SUT e, em seguida, inicializamosRestAssuredMockMvc no modo autônomo como acima:
@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);
}
Como inicializamosRestAssuredMockMvc estaticamente em nosso método@Before, não há necessidade de inicializá-lo em cada teste.
Standalone mode is great for unit tests because it only initializes the controllers that we provide, em vez de todo o contexto do aplicativo. Isso mantém nossos testes rápidos.
Agora, vamos ver um exemplo de teste:
@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("[]")));
}
InicializarRestAssuredMockMvc com nosso@ControllerAdvice além de nosso@RestController nos permite testar nossos cenários de exceção também:
@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());
}
Como visto acima, o REST-assegurado usa o formato de cenário conhecido quando e quando para definir o teste:
-
given() - especifica os detalhes da solicitação HTTP
-
when() - especifica o verbo HTTP, bem como a rota
-
then() - valida a resposta HTTP
6. Teste de integração do controlador REST com garantia REST
Também podemos usarRestAssuredMockMvc com as ferramentas de teste do Spring para nossos testes de integração.
Primeiro, configuramos nossa classe de teste com@RunWith(SpringRunner.class)e@SpringBootTest(webEnvironment = RANDOM_PORT):
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
public class CourseControllerIntegrationTest {
//...
}
Isso executará nosso teste com o contexto do aplicativo configurado em nossa classe@SpringBootApplication em uma porta aleatória.
Em seguida, injetamos nossoWebApplicationContext e o usamos para inicializarRestAssuredMockMvc como acima:
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void initialiseRestAssuredMockMvcWebApplicationContext() {
RestAssuredMockMvc.webAppContextSetup(webApplicationContext);
}
Agora que temos nossa classe de teste configurada eRestAssuredMockMvc inicializado, estamos prontos para começar a escrever nossos testes:
@Test
public void givenNoMatchingCourseCodeWhenGetCourseThenRespondWithStatusNotFound() {
String nonMatchingCourseCode = "nonMatchingCourseCode";
given()
.when()
.get("/courses/" + nonMatchingCourseCode)
.then()
.log().ifValidationFails()
.statusCode(NOT_FOUND.value());
}
Lembre-se, uma vez que inicializamosRestAssuredMockMvc estaticamente em nosso método@Before, não há necessidade de inicializá-lo em cada teste.
Para um mergulho mais profundo na API garantida por REST, verifique nossoREST-assured Guide.
7. Conclusão
Neste tutorial, vimos como podemos usar a garantia de REST para testar nosso aplicativo Spring MVC usando o módulospring-mock-mvc da garantia de REST.
InicializandoRestAssuredMockMvc emstandalone mode is great for unit testing, pois ele inicializa apenas oControllers fornecido, mantendo nossos testes rápidos.
InicializandoRestAssuredMockMvc emweb application context mode is great for integration testing, pois usa nossoWebApplicationContext completo.
Como sempre, você pode encontrar todos os nossos códigos de amostraover on Github.