Files
awesome-copilot/skills/spring-boot-testing/references/resttestclient.md
Kartik Dhiman e4fc57f204 feat: add spring-boot-testing skill for Spring Boot 4.0 (#1085)
- Introduced MockMvcTester for AssertJ-style assertions in Spring MVC testing.
- Added @RestClientTest for testing REST clients with MockRestServiceServer.
- Implemented RestTestClient as a modern alternative to TestRestTemplate.
- Documented migration steps from Spring Boot 3.x to 4.0, including dependency and annotation changes.
- Created an overview of test slices to guide testing strategies.
- Included Testcontainers setup for JDBC testing with PostgreSQL and MySQL.
- Enhanced @WebMvcTest documentation with examples for various HTTP methods and validation.
2026-03-20 10:24:37 +11:00

279 lines
4.9 KiB
Markdown

# RestTestClient
Modern REST client testing with Spring Boot 4+ (replaces TestRestTemplate).
## Overview
RestTestClient is the modern alternative to TestRestTemplate in Spring Boot 4.0+. It provides a fluent, reactive API for testing REST endpoints.
## Setup
### Dependency (Spring Boot 4+)
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-restclient-test</artifactId>
<scope>test</scope>
</dependency>
```
### Basic Configuration
```java
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class OrderIntegrationTest {
@Autowired
private RestTestClient restClient;
}
```
## HTTP Methods
### GET Request
```java
@Test
void shouldGetOrder() {
restClient
.get()
.uri("/orders/1")
.exchange()
.expectStatus()
.isOk()
.expectBody(Order.class)
.value(order -> {
assertThat(order.getId()).isEqualTo(1L);
assertThat(order.getStatus()).isEqualTo("PENDING");
});
}
```
### POST Request
```java
@Test
void shouldCreateOrder() {
OrderRequest request = new OrderRequest("Laptop", 2);
restClient
.post()
.uri("/orders")
.contentType(MediaType.APPLICATION_JSON)
.body(request)
.exchange()
.expectStatus()
.isCreated()
.expectHeader()
.location("/orders/1")
.expectBody(Long.class)
.isEqualTo(1L);
}
```
### PUT Request
```java
@Test
void shouldUpdateOrder() {
restClient
.put()
.uri("/orders/1")
.body(new OrderUpdate("COMPLETED"))
.exchange()
.expectStatus()
.isOk();
}
```
### DELETE Request
```java
@Test
void shouldDeleteOrder() {
restClient
.delete()
.uri("/orders/1")
.exchange()
.expectStatus()
.isNoContent();
}
```
## Response Assertions
### Status Codes
```java
restClient
.get()
.uri("/orders/1")
.exchange()
.expectStatus()
.isOk() // 200
.isCreated() // 201
.isNoContent() // 204
.isBadRequest() // 400
.isNotFound() // 404
.is5xxServerError() // 5xx
.isEqualTo(200); // Specific code
```
### Headers
```java
restClient
.post()
.uri("/orders")
.exchange()
.expectHeader()
.location("/orders/1")
.contentType(MediaType.APPLICATION_JSON)
.exists("X-Request-Id")
.valueEquals("X-Api-Version", "v1");
```
### Body Assertions
```java
restClient
.get()
.uri("/orders/1")
.exchange()
.expectBody(Order.class)
.value(order -> assertThat(order.getId()).isEqualTo(1L))
.returnResult();
```
### JSON Path
```java
restClient
.get()
.uri("/orders")
.exchange()
.expectBody()
.jsonPath("$.content[0].id").isEqualTo(1)
.jsonPath("$.content[0].status").isEqualTo("PENDING")
.jsonPath("$.totalElements").isNumber();
```
## Request Configuration
### Headers
```java
restClient
.get()
.uri("/orders/1")
.header("Authorization", "Bearer token")
.header("X-Api-Key", "secret")
.exchange();
```
### Query Parameters
```java
restClient
.get()
.uri(uriBuilder -> uriBuilder
.path("/orders")
.queryParam("status", "PENDING")
.queryParam("page", 0)
.queryParam("size", 10)
.build())
.exchange();
```
### Path Variables
```java
restClient
.get()
.uri("/orders/{id}", 1L)
.exchange();
```
## With MockMvc
RestTestClient can also work with MockMvc (no server startup):
```java
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestTestClient
class OrderMockMvcTest {
@Autowired
private RestTestClient restClient;
@Test
void shouldWorkWithMockMvc() {
// Uses MockMvc under the hood - no server startup
restClient
.get()
.uri("/orders/1")
.exchange()
.expectStatus()
.isOk();
}
}
```
## Comparison: RestTestClient vs TestRestTemplate
| Feature | RestTestClient | TestRestTemplate |
| ------- | -------------- | ---------------- |
| Style | Fluent/reactive | Imperative |
| Spring Boot | 4.0+ | All versions (deprecated in 4) |
| Assertions | Built-in | Manual |
| MockMvc support | Yes | No |
| Async | Native | Requires extra handling |
## Migration from TestRestTemplate
### Before (Deprecated)
```java
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldGetOrder() {
ResponseEntity<Order> response = restTemplate
.getForEntity("/orders/1", Order.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getId()).isEqualTo(1L);
}
```
### After (RestTestClient)
```java
@Autowired
private RestTestClient restClient;
@Test
void shouldGetOrder() {
restClient
.get()
.uri("/orders/1")
.exchange()
.expectStatus()
.isOk()
.expectBody(Order.class)
.value(order -> assertThat(order.getId()).isEqualTo(1L));
}
```
## Best Practices
1. Use with @SpringBootTest(WebEnvironment.RANDOM_PORT) for real HTTP
2. Use with @AutoConfigureMockMvc for faster tests without server
3. Leverage fluent assertions for readability
4. Test both success and error scenarios
5. Verify headers for security/API versioning