Spring Boot测试中使用REST Assured(转)
Posted 奋斗终生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot测试中使用REST Assured(转)相关的知识,希望对你有一定的参考价值。
原文:https://rieckpil.de/testing-spring-boot-applications-with-rest-assured/
REST Assured is a Java DSL (Domain Specific Langauge) that aims to simplify testing REST APIs. It follows a BDD (Behavior Driven Development) approach and is influenced by testing APIs with dynamic languages like Groovy. We can use REST Assured to test the REST API of any Java project as it is framework independent. However, REST Assured comes with a great Spring integration for testing our @RestController
endpoints that we\'re about to explore with this article.
Spring Boot and REST Assured Project Setup
For our demo application we use Java 11, Spring Boot 2.4.0, and the following dependencies:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>de.rieckpil.blog</groupId>
<artifactId>spring-boot-rest-assured</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-rest-assured</name>
<properties>
<java.version>11</java.version>
<groovy.version>3.0.7</groovy.version>
<rest-assured.version>4.3.3</rest-assured.version>
</properties>
<dependencies>
<!-- Spring Boot Starters for Web, Validation, and Security -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>spring-mock-mvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- Spring Boot Maven Plugin -->
</build>
</project>
|
As Spring Boot manages the dependency version of REST Assured and all its modules, we can define the spring-mock-mvc
without an explicit version. We can override the managed version consistently with the rest-assured.version
property.
If we mix up our dependency versions for REST Assured and try to outsmart Spring Boot, we might see several exceptions during test runtime:
1
2
3
4
5
|
java.lang.NoClassDefFoundError: io/restassured/internal/common/assertion/AssertParameter
at io.restassured.module.mockmvc.config.MockMvcParamConfig.<init>(MockMvcParamConfig.java:65)
at io.restassured.module.mockmvc.config.MockMvcParamConfig.<init>(MockMvcParamConfig.java:43)
at io.restassured.module.mockmvc.config.RestAssuredMockMvcConfig.<init>(RestAssuredMockMvcConfig.java:56)
|
As REST Assured has quite some transitive dependencies (Groovy related dependencies, Apache HttpClient, Hamcrest, and Tagsoup), we should favor the manged version from our Spring Boot release.
As I\'ve seen several integration issues for REST Assured and Java 11 projects, consider the following tips:
- IntelliJ IDEA can help to identify duplicated dependency includes for a Maven project. We can open a visual dependency graph with the following steps: right-click inside our
pom.xml
-> Maven -> Show Dependencies. A red arrow indicates a violation for dependency convergence. The Maven Enforcer plugin can even detect this as part of your build. - REST Assured also provides a
rest-assured-all
dependency, that can help to solve split package problems. - When using Spring Boot together with REST Assured, you might also stumble over exceptions for Groovy related parts of this testing library. Explicitly overriding Spring Boot\'s default Groovy version with
groovy.version
inside thepom.xml
seems to help here.
The Spring MVC RestController Under Test
Our sample application is a book store that exposes the following REST API endpoints to manage its inventory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public List<Book> getAllBooks(@RequestParam(value = "amount", defaultValue = "500") int amount) {
return bookService.getAllBooks(amount);
}
@GetMapping("/{id}")
public ResponseEntity<Book> getBookById(@PathVariable("id") Long id) {
return ResponseEntity.ok(bookService.getBookById(id));
}
@PostMapping
public ResponseEntity<Void> createNewBook(
@Valid @RequestBody BookRequest bookRequest,
UriComponentsBuilder uriComponentsBuilder) {
Long bookId = bookService.createNewBook(bookRequest);
UriComponents uriComponents = uriComponentsBuilder.path("/api/books/{id}").buildAndExpand(bookId);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(uriComponents.toUri());
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
}
|
The actual implementation of the BookService
doesn\'t matter for this demonstration and we can assume it stores our book entities somewhere (e.g. database or in-memory).
For a more realistic example, we\'ll secure the HTTP POST endpoint and only allow authenticated users that have the ADMIN
role to access it:
JUnit 5 & Mockito Cheat Sheet
Answering 24 questions for the two most essential Java testing libraries.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.httpBasic()
.and()
.authorizeRequests(
requests -> requests
.mvcMatchers(HttpMethod.GET, "/api/books").permitAll()
.mvcMatchers(HttpMethod.GET, "/api/books/*").permitAll()
.mvcMatchers(HttpMethod.POST, "/api/books").hasRole("ADMIN")
.anyRequest().authenticated());
}
}
|
REST Assured Powered MockMvc Test
There are multiple ways to configure REST Assured for a Spring MVC test case. We can manually pass a list of controller objects, provide a MockMvcBuilder
, pass a WebApplicationContext
or a MockMvc
instance.
As we can inject an already initialized MockMvc
bean to our test when using Spring Boot\'s test slice annotation @WebMvcTest
, we\'ll use it for our REST Assured configuration.
1
2
3
4
|
@BeforeEach
void setUp() {
RestAssuredMockMvc.mockMvc(mockMvc);
}
|
All upcoming requests for this test class will target this MockMvc
instance unless we override it explicitly.
With this setup in place, we can write our first test that ensures unauthenticated clients can request our book store\'s inventory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Test
void shouldAllowBookRetrievalWithoutAuthentication() {
Mockito.when(bookService.getAllBooks(42)).thenReturn(
List.of(new Book(42L, "42", "REST Assured With Spring Boot", "Duke")));
RestAssuredMockMvc
.given()
.auth().none()
.param("amount", 42)
.when()
.get("/api/books")
.then()
.statusCode(200)
.body("$.size()", Matchers.equalTo(1))
.body("[0].id", Matchers.equalTo(42))
.body("[0].isbn", Matchers.equalTo("42"))
.body("[0].author", Matchers.equalTo("Duke"))
.body("[0].title", Matchers.equalTo("REST Assured With Spring Boot"));
}
|
Due to REST Assured\'s fluent API, one should easily grasp the test setup above. REST Assured also follows a BDD style and .given().when().then()
gives each request a standardized schema.
For writing expectations for our HTTP response body/status/header, we then use Matchers
from Hamcrest.
The test above doesn\'t use static imports because there is a small pitfall. When importing the .given()
method from REST Assured, we have to make sure it\'s not the traditional non-Spring variant:
1
2
3
4
5
6
|
// import this when using static imports
import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
// NOT this
import static io.restassured.RestAssured.given;
|
Furthermore, there is a potential second pitfall when using a static import and Mockito in the same test.
For tests where we don\'t need a .given()
step (e.g., we don\'t pass any query parameter/path variable/header), we can omit it and start our request specification with .when()
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Test
void 以上是关于Spring Boot测试中使用REST Assured(转)的主要内容,如果未能解决你的问题,请参考以下文章
使用带有 Spring Boot 的 Spock 测试框架来测试我的 REST 控制器 使用 restAssured 测试 Spring Boot Rest 应用程序 |