or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotation-controllers.mdconfiguration.mderror-handling.mdfunctional-routing.mdindex.mdserver-configuration.mdtesting.mdwebclient.md

testing.mddocs/

0

# Testing

1

2

Spring Boot WebFlux provides comprehensive testing support through WebTestClient and reactive testing utilities. This enables full integration testing of reactive web applications with support for mocking, assertions, and test-specific configurations.

3

4

## WebTestClient Interface

5

6

### Core WebTestClient

7

8

```java { .api }

9

public interface WebTestClient {

10

11

RequestHeadersUriSpec<?> get();

12

RequestBodyUriSpec post();

13

RequestBodyUriSpec put();

14

RequestBodyUriSpec patch();

15

RequestHeadersUriSpec<?> delete();

16

RequestHeadersUriSpec<?> options();

17

RequestHeadersUriSpec<?> head();

18

19

RequestBodyUriSpec method(HttpMethod method);

20

21

Builder mutate();

22

23

static Builder bindToServer();

24

static Builder bindToController(Object... controllers);

25

static Builder bindToApplicationContext(ApplicationContext applicationContext);

26

static Builder bindToRouterFunction(RouterFunction<?> routerFunction);

27

static Builder bindToWebHandler(WebHandler webHandler);

28

static Builder bindToWebServer(int port);

29

30

interface Builder {

31

Builder baseUrl(String baseUrl);

32

Builder defaultHeader(String header, String... values);

33

Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);

34

Builder defaultCookie(String cookie, String... values);

35

Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);

36

Builder filter(ExchangeFilterFunction filter);

37

Builder filters(Consumer<List<ExchangeFilterFunction>> filtersConsumer);

38

Builder clientConnector(ClientHttpConnector connector);

39

Builder codecs(Consumer<ClientCodecConfigurer> configurer);

40

Builder exchangeStrategies(ExchangeStrategies strategies);

41

Builder responseTimeout(Duration timeout);

42

43

WebTestClient build();

44

}

45

}

46

```

47

48

## Request Specification Interfaces

49

50

### RequestHeadersUriSpec

51

52

```java { .api }

53

public interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>> extends UriSpec<S> {

54

55

}

56

57

public interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> {

58

S header(String headerName, String... headerValues);

59

S headers(Consumer<HttpHeaders> headersConsumer);

60

S accept(MediaType... acceptableMediaTypes);

61

S acceptCharset(Charset... acceptableCharsets);

62

S cookie(String name, String value);

63

S cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);

64

S ifModifiedSince(ZonedDateTime ifModifiedSince);

65

S ifNoneMatch(String... ifNoneMatches);

66

67

ResponseSpec exchange();

68

}

69

```

70

71

### RequestBodyUriSpec

72

73

```java { .api }

74

public interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {

75

76

}

77

78

public interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {

79

RequestHeadersSpec<?> contentLength(long contentLength);

80

RequestHeadersSpec<?> contentType(MediaType contentType);

81

82

RequestHeadersSpec<?> body(BodyInserter<?, ? super ClientHttpRequest> inserter);

83

<T> RequestHeadersSpec<?> body(Mono<T> body, Class<T> elementClass);

84

<T> RequestHeadersSpec<?> body(Mono<T> body, ParameterizedTypeReference<T> elementTypeRef);

85

<T> RequestHeadersSpec<?> body(Flux<T> body, Class<T> elementClass);

86

<T> RequestHeadersSpec<?> body(Flux<T> body, ParameterizedTypeReference<T> elementTypeRef);

87

RequestHeadersSpec<?> body(Object body);

88

89

RequestHeadersSpec<?> bodyValue(Object body);

90

}

91

```

92

93

## Response Specification and Assertions

94

95

### ResponseSpec

96

97

```java { .api }

98

public interface ResponseSpec {

99

100

StatusAssertions expectStatus();

101

HeaderAssertions expectHeader();

102

JsonPathAssertions expectJsonPath(String expression, Object... args);

103

XPathAssertions expectXPath(String expression, Object... args, Map<String, String> namespaces);

104

BodySpec<String, ?> expectBody(String content);

105

<T> BodySpec<T, ?> expectBody(Class<T> bodyType);

106

<T> BodySpec<T, ?> expectBody(ParameterizedTypeReference<T> bodyType);

107

<T> ListBodySpec<T> expectBodyList(Class<T> elementType);

108

<T> ListBodySpec<T> expectBodyList(ParameterizedTypeReference<T> elementType);

109

110

ResponseSpec expectBody();

111

112

<T> FluxExchangeResult<T> returnResult(Class<T> elementType);

113

<T> FluxExchangeResult<T> returnResult(ParameterizedTypeReference<T> elementTypeRef);

114

}

115

```

116

117

### StatusAssertions

118

119

```java { .api }

120

public interface StatusAssertions {

121

WebTestClient.ResponseSpec isOk();

122

WebTestClient.ResponseSpec isCreated();

123

WebTestClient.ResponseSpec isAccepted();

124

WebTestClient.ResponseSpec isNoContent();

125

WebTestClient.ResponseSpec isBadRequest();

126

WebTestClient.ResponseSpec isUnauthorized();

127

WebTestClient.ResponseSpec isForbidden();

128

WebTestClient.ResponseSpec isNotFound();

129

WebTestClient.ResponseSpec isEqualTo(HttpStatus status);

130

WebTestClient.ResponseSpec isEqualTo(int status);

131

WebTestClient.ResponseSpec is1xxInformational();

132

WebTestClient.ResponseSpec is2xxSuccessful();

133

WebTestClient.ResponseSpec is3xxRedirection();

134

WebTestClient.ResponseSpec is4xxClientError();

135

WebTestClient.ResponseSpec is5xxServerError();

136

}

137

```

138

139

## Testing Examples

140

141

### Controller Testing

142

143

```java

144

@ExtendWith(SpringExtension.class)

145

@WebFluxTest(UserController.class)

146

class UserControllerTest {

147

148

@Autowired

149

private WebTestClient webTestClient;

150

151

@MockBean

152

private UserService userService;

153

154

@Test

155

void shouldGetUser() {

156

// Given

157

User user = new User("1", "John Doe", "john@example.com");

158

when(userService.findById("1")).thenReturn(Mono.just(user));

159

160

// When & Then

161

webTestClient.get()

162

.uri("/api/users/{id}", "1")

163

.accept(MediaType.APPLICATION_JSON)

164

.exchange()

165

.expectStatus().isOk()

166

.expectHeader().contentType(MediaType.APPLICATION_JSON)

167

.expectBody(User.class)

168

.value(u -> {

169

assertThat(u.getId()).isEqualTo("1");

170

assertThat(u.getName()).isEqualTo("John Doe");

171

assertThat(u.getEmail()).isEqualTo("john@example.com");

172

});

173

}

174

175

@Test

176

void shouldCreateUser() {

177

// Given

178

User newUser = new User(null, "Jane Smith", "jane@example.com");

179

User savedUser = new User("2", "Jane Smith", "jane@example.com");

180

when(userService.save(any(User.class))).thenReturn(Mono.just(savedUser));

181

182

// When & Then

183

webTestClient.post()

184

.uri("/api/users")

185

.contentType(MediaType.APPLICATION_JSON)

186

.body(Mono.just(newUser), User.class)

187

.exchange()

188

.expectStatus().isCreated()

189

.expectBody(User.class)

190

.value(u -> assertThat(u.getId()).isEqualTo("2"));

191

}

192

193

@Test

194

void shouldGetAllUsers() {

195

// Given

196

List<User> users = Arrays.asList(

197

new User("1", "John Doe", "john@example.com"),

198

new User("2", "Jane Smith", "jane@example.com")

199

);

200

when(userService.findAll()).thenReturn(Flux.fromIterable(users));

201

202

// When & Then

203

webTestClient.get()

204

.uri("/api/users")

205

.accept(MediaType.APPLICATION_JSON)

206

.exchange()

207

.expectStatus().isOk()

208

.expectBodyList(User.class)

209

.hasSize(2)

210

.contains(users.get(0), users.get(1));

211

}

212

}

213

```

214

215

### Router Function Testing

216

217

```java

218

@ExtendWith(SpringExtension.class)

219

@WebFluxTest

220

class UserRouterTest {

221

222

@Autowired

223

private WebTestClient webTestClient;

224

225

@MockBean

226

private UserHandler userHandler;

227

228

@TestConfiguration

229

static class TestConfig {

230

@Bean

231

@Primary

232

RouterFunction<ServerResponse> testRoutes(UserHandler handler) {

233

return RouterFunctions.route()

234

.GET("/api/users/{id}", handler::getUser)

235

.GET("/api/users", handler::listUsers)

236

.POST("/api/users", handler::createUser)

237

.build();

238

}

239

}

240

241

@Test

242

void shouldRouteToGetUser() {

243

// Given

244

ServerRequest request = MockServerRequest.builder()

245

.method(HttpMethod.GET)

246

.uri(URI.create("/api/users/1"))

247

.pathVariable("id", "1")

248

.build();

249

250

User user = new User("1", "John Doe", "john@example.com");

251

when(userHandler.getUser(any(ServerRequest.class)))

252

.thenReturn(ServerResponse.ok().body(Mono.just(user), User.class));

253

254

// When & Then

255

webTestClient.get()

256

.uri("/api/users/{id}", "1")

257

.exchange()

258

.expectStatus().isOk();

259

}

260

}

261

```

262

263

### Integration Testing

264

265

```java

266

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

267

class UserIntegrationTest {

268

269

@Autowired

270

private WebTestClient webTestClient;

271

272

@Autowired

273

private UserRepository userRepository;

274

275

@BeforeEach

276

void setUp() {

277

userRepository.deleteAll().block();

278

}

279

280

@Test

281

void shouldPerformFullUserLifecycle() {

282

// Create user

283

User newUser = new User(null, "Integration Test", "integration@example.com");

284

285

User createdUser = webTestClient.post()

286

.uri("/api/users")

287

.contentType(MediaType.APPLICATION_JSON)

288

.body(Mono.just(newUser), User.class)

289

.exchange()

290

.expectStatus().isCreated()

291

.expectBody(User.class)

292

.returnResult()

293

.getResponseBody();

294

295

assertThat(createdUser.getId()).isNotNull();

296

297

// Get user

298

webTestClient.get()

299

.uri("/api/users/{id}", createdUser.getId())

300

.exchange()

301

.expectStatus().isOk()

302

.expectBody(User.class)

303

.value(u -> {

304

assertThat(u.getId()).isEqualTo(createdUser.getId());

305

assertThat(u.getName()).isEqualTo("Integration Test");

306

});

307

308

// Update user

309

User updatedUser = new User(createdUser.getId(), "Updated Name", "updated@example.com");

310

311

webTestClient.put()

312

.uri("/api/users/{id}", createdUser.getId())

313

.contentType(MediaType.APPLICATION_JSON)

314

.body(Mono.just(updatedUser), User.class)

315

.exchange()

316

.expectStatus().isOk()

317

.expectBody(User.class)

318

.value(u -> assertThat(u.getName()).isEqualTo("Updated Name"));

319

320

// Delete user

321

webTestClient.delete()

322

.uri("/api/users/{id}", createdUser.getId())

323

.exchange()

324

.expectStatus().isNoContent();

325

326

// Verify deletion

327

webTestClient.get()

328

.uri("/api/users/{id}", createdUser.getId())

329

.exchange()

330

.expectStatus().isNotFound();

331

}

332

}

333

```

334

335

### Custom WebTestClient Configuration

336

337

```java

338

@TestConfiguration

339

public class WebTestClientConfig {

340

341

@Bean

342

@Primary

343

public WebTestClient customWebTestClient() {

344

return WebTestClient.bindToServer()

345

.baseUrl("http://localhost:8080")

346

.responseTimeout(Duration.ofSeconds(30))

347

.filter(logRequest())

348

.filter(logResponse())

349

.defaultHeader("Accept", MediaType.APPLICATION_JSON_VALUE)

350

.defaultCookie("SESSION", "test-session")

351

.build();

352

}

353

354

private ExchangeFilterFunction logRequest() {

355

return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {

356

System.out.println("Request: " + clientRequest.method() + " " + clientRequest.url());

357

clientRequest.headers().forEach((name, values) ->

358

values.forEach(value -> System.out.println(name + ": " + value))

359

);

360

return Mono.just(clientRequest);

361

});

362

}

363

364

private ExchangeFilterFunction logResponse() {

365

return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {

366

System.out.println("Response: " + clientResponse.statusCode());

367

return Mono.just(clientResponse);

368

});

369

}

370

}

371

```

372

373

### Testing Reactive Streams

374

375

```java

376

@Test

377

void shouldTestReactiveStream() {

378

Flux<String> source = Flux.just("foo", "bar", "baz");

379

380

StepVerifier.create(source)

381

.expectNext("foo")

382

.expectNext("bar")

383

.expectNext("baz")

384

.verifyComplete();

385

}

386

387

@Test

388

void shouldTestErrorHandling() {

389

Flux<String> source = Flux.just("foo", "bar")

390

.concatWith(Flux.error(new RuntimeException("boom")));

391

392

StepVerifier.create(source)

393

.expectNext("foo")

394

.expectNext("bar")

395

.expectError(RuntimeException.class)

396

.verify();

397

}

398

399

@Test

400

void shouldTestWithVirtualTime() {

401

StepVerifier.withVirtualTime(() ->

402

Flux.interval(Duration.ofHours(4)).take(2))

403

.expectSubscription()

404

.expectNoEvent(Duration.ofHours(4))

405

.expectNext(0L)

406

.thenAwait(Duration.ofHours(4))

407

.expectNext(1L)

408

.verifyComplete();

409

}

410

```

411

412

### Test Slices

413

414

```java

415

// Test only WebFlux layer

416

@WebFluxTest(UserController.class)

417

class WebFluxLayerTest {

418

// Only WebFlux components are loaded

419

}

420

421

// Test with custom auto-configuration

422

@WebFluxTest(

423

controllers = UserController.class,

424

excludeAutoConfiguration = SecurityAutoConfiguration.class

425

)

426

class CustomWebFluxTest {

427

// WebFlux with custom configuration

428

}

429

430

// Test specific components

431

@TestConfiguration

432

static class TestConfig {

433

@Bean

434

@Primary

435

public UserService mockUserService() {

436

return Mockito.mock(UserService.class);

437

}

438

}

439

```