or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdmockmvc-integration.mdreactive-testing.mdsecurity-context-annotations.mdtest-context-management.md

reactive-testing.mddocs/

0

# Reactive Testing (WebTestClient)

1

2

WebTestClient integration for testing security in reactive Spring WebFlux applications, providing mutators for various authentication scenarios including OAuth2, JWT, and OIDC. This integration enables comprehensive testing of reactive security configurations and authentication flows.

3

4

## Setup

5

6

### WebTestClient Configuration

7

8

Configure WebTestClient with Spring Security support for reactive applications.

9

10

```java { .api }

11

/**

12

* Security configurers for reactive testing

13

*/

14

public class SecurityMockServerConfigurers {

15

16

/**

17

* Configure Spring Security with WebTestClient

18

* @return MockServerConfigurer for reactive security integration

19

*/

20

public static MockServerConfigurer springSecurity();

21

}

22

```

23

24

**Usage Examples:**

25

26

```java

27

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

28

public class ReactiveSecurityTest {

29

30

@Autowired

31

private WebTestClient webTestClient;

32

33

// WebTestClient is auto-configured with Spring Security in Spring Boot tests

34

35

@Test

36

public void testReactiveEndpoint() {

37

webTestClient

38

.mutateWith(mockUser())

39

.get().uri("/reactive/secure")

40

.exchange()

41

.expectStatus().isOk();

42

}

43

}

44

```

45

46

## Authentication Mutators

47

48

### Basic User Authentication

49

50

Create user-based authentication for reactive testing.

51

52

```java { .api }

53

/**

54

* Mock authentication with specific Authentication object

55

* @param authentication The authentication to use

56

* @return Mutator that applies the authentication

57

*/

58

public static <T> T mockAuthentication(Authentication authentication);

59

60

/**

61

* Mock user from UserDetails

62

* @param userDetails The UserDetails to use

63

* @return Mutator that applies user authentication

64

*/

65

public static <T> T mockUser(UserDetails userDetails);

66

67

/**

68

* Mock default user with username "user" and role "USER"

69

* @return UserExchangeMutator for configuration

70

*/

71

public static UserExchangeMutator mockUser();

72

73

/**

74

* Mock user with specific username

75

* @param username The username for authentication

76

* @return UserExchangeMutator for configuration

77

*/

78

public static UserExchangeMutator mockUser(String username);

79

80

/**

81

* Fluent configuration for user authentication in reactive tests

82

*/

83

public interface UserExchangeMutator extends WebTestClientConfigurer, MockServerConfigurer {

84

/** Set user password */

85

UserExchangeMutator password(String password);

86

87

/** Set user roles (automatically prefixed with "ROLE_") */

88

UserExchangeMutator roles(String... roles);

89

90

/** Set granted authorities */

91

UserExchangeMutator authorities(GrantedAuthority... authorities);

92

}

93

```

94

95

**Usage Examples:**

96

97

```java

98

@Test

99

public void testWithDefaultUser() {

100

webTestClient

101

.mutateWith(mockUser()) // username="user", roles=["USER"]

102

.get().uri("/api/user-data")

103

.exchange()

104

.expectStatus().isOk();

105

}

106

107

@Test

108

public void testWithCustomUser() {

109

webTestClient

110

.mutateWith(mockUser("alice")

111

.roles("USER", "MANAGER")

112

.password("secret"))

113

.get().uri("/api/manager-data")

114

.exchange()

115

.expectStatus().isOk();

116

}

117

118

@Test

119

public void testWithUserDetails() {

120

UserDetails user = User.builder()

121

.username("bob")

122

.password("password")

123

.authorities("ROLE_ADMIN", "READ_PRIVILEGES")

124

.build();

125

126

webTestClient

127

.mutateWith(mockUser(user))

128

.get().uri("/api/admin-data")

129

.exchange()

130

.expectStatus().isOk();

131

}

132

133

@Test

134

public void testWithCustomAuthentication() {

135

Authentication auth = new UsernamePasswordAuthenticationToken(

136

"customuser",

137

"credentials",

138

Arrays.asList(new SimpleGrantedAuthority("CUSTOM_AUTHORITY"))

139

);

140

141

webTestClient

142

.mutateWith(mockAuthentication(auth))

143

.get().uri("/api/custom")

144

.exchange()

145

.expectStatus().isOk();

146

}

147

```

148

149

### JWT Authentication

150

151

Comprehensive JWT authentication support for reactive applications.

152

153

```java { .api }

154

/**

155

* Mock JWT authentication with default configuration

156

* @return JwtMutator for configuration

157

*/

158

public static JwtMutator mockJwt();

159

160

/**

161

* Fluent configuration for JWT authentication in reactive tests

162

*/

163

public interface JwtMutator extends WebTestClientConfigurer, MockServerConfigurer {

164

/**

165

* Configure JWT using builder consumer

166

* @param jwtBuilderConsumer Consumer to configure JWT builder

167

* @return JwtMutator for chaining

168

*/

169

JwtMutator jwt(Consumer<Jwt.Builder> jwtBuilderConsumer);

170

171

/**

172

* Use specific JWT instance

173

* @param jwt The JWT to use

174

* @return JwtMutator for chaining

175

*/

176

JwtMutator jwt(Jwt jwt);

177

178

/**

179

* Set authorities for JWT authentication

180

* @param authorities The granted authorities

181

* @return JwtMutator for chaining

182

*/

183

JwtMutator authorities(GrantedAuthority... authorities);

184

}

185

```

186

187

**Usage Examples:**

188

189

```java

190

@Test

191

public void testWithJwt() {

192

webTestClient

193

.mutateWith(mockJwt()

194

.jwt(jwt -> jwt

195

.claim("sub", "user123")

196

.claim("scope", "read write")

197

.claim("aud", "my-app"))

198

.authorities(

199

new SimpleGrantedAuthority("SCOPE_read"),

200

new SimpleGrantedAuthority("SCOPE_write")

201

))

202

.get().uri("/api/jwt-protected")

203

.exchange()

204

.expectStatus().isOk();

205

}

206

207

@Test

208

public void testWithPrebuiltJwt() {

209

Jwt jwt = Jwt.withTokenValue("token-value")

210

.header("alg", "HS256")

211

.claim("sub", "user456")

212

.claim("scope", "admin")

213

.build();

214

215

webTestClient

216

.mutateWith(mockJwt().jwt(jwt))

217

.get().uri("/api/admin-jwt")

218

.exchange()

219

.expectStatus().isOk();

220

}

221

```

222

223

### OAuth2 Opaque Token

224

225

Support for OAuth2 opaque token authentication in reactive applications.

226

227

```java { .api }

228

/**

229

* Mock OAuth2 opaque token authentication

230

* @return OpaqueTokenMutator for configuration

231

*/

232

public static OpaqueTokenMutator mockOpaqueToken();

233

234

/**

235

* Fluent configuration for opaque token authentication

236

*/

237

public interface OpaqueTokenMutator extends WebTestClientConfigurer, MockServerConfigurer {

238

/**

239

* Set token attributes

240

* @param attributes Map of token attributes

241

* @return OpaqueTokenMutator for chaining

242

*/

243

OpaqueTokenMutator attributes(Map<String, Object> attributes);

244

245

/**

246

* Configure token attributes using consumer

247

* @param attributesConsumer Consumer to configure attributes

248

* @return OpaqueTokenMutator for chaining

249

*/

250

OpaqueTokenMutator attributes(Consumer<Map<String, Object>> attributesConsumer);

251

252

/**

253

* Set authorities for token authentication

254

* @param authorities The granted authorities

255

* @return OpaqueTokenMutator for chaining

256

*/

257

OpaqueTokenMutator authorities(GrantedAuthority... authorities);

258

}

259

```

260

261

**Usage Examples:**

262

263

```java

264

@Test

265

public void testWithOpaqueToken() {

266

webTestClient

267

.mutateWith(mockOpaqueToken()

268

.attributes(attrs -> {

269

attrs.put("sub", "user789");

270

attrs.put("scope", "read write");

271

attrs.put("client_id", "my-client");

272

})

273

.authorities(

274

new SimpleGrantedAuthority("SCOPE_read"),

275

new SimpleGrantedAuthority("SCOPE_write")

276

))

277

.get().uri("/api/token-secured")

278

.exchange()

279

.expectStatus().isOk();

280

}

281

```

282

283

### OAuth2 Login

284

285

OAuth2 login authentication support for reactive applications.

286

287

```java { .api }

288

/**

289

* Mock OAuth2 login authentication

290

* @return OAuth2LoginMutator for configuration

291

*/

292

public static OAuth2LoginMutator mockOAuth2Login();

293

294

/**

295

* Fluent configuration for OAuth2 login authentication

296

*/

297

public interface OAuth2LoginMutator extends WebTestClientConfigurer, MockServerConfigurer {

298

/**

299

* Set OAuth2 user attributes

300

* @param attributes Map of user attributes

301

* @return OAuth2LoginMutator for chaining

302

*/

303

OAuth2LoginMutator attributes(Map<String, Object> attributes);

304

305

/**

306

* Configure OAuth2 user attributes using consumer

307

* @param attributesConsumer Consumer to configure attributes

308

* @return OAuth2LoginMutator for chaining

309

*/

310

OAuth2LoginMutator attributes(Consumer<Map<String, Object>> attributesConsumer);

311

312

/**

313

* Set client registration ID

314

* @param clientRegistrationId The client registration ID

315

* @return OAuth2LoginMutator for chaining

316

*/

317

OAuth2LoginMutator clientRegistrationId(String clientRegistrationId);

318

319

/**

320

* Set authorities for OAuth2 authentication

321

* @param authorities The granted authorities

322

* @return OAuth2LoginMutator for chaining

323

*/

324

OAuth2LoginMutator authorities(GrantedAuthority... authorities);

325

}

326

```

327

328

**Usage Examples:**

329

330

```java

331

@Test

332

public void testWithOAuth2Login() {

333

webTestClient

334

.mutateWith(mockOAuth2Login()

335

.clientRegistrationId("google")

336

.attributes(attrs -> {

337

attrs.put("sub", "12345");

338

attrs.put("name", "John Doe");

339

attrs.put("email", "john@example.com");

340

})

341

.authorities(new SimpleGrantedAuthority("ROLE_USER")))

342

.get().uri("/oauth2/user")

343

.exchange()

344

.expectStatus().isOk();

345

}

346

```

347

348

### OIDC Login

349

350

OpenID Connect login authentication support for reactive applications.

351

352

```java { .api }

353

/**

354

* Mock OIDC login authentication

355

* @return OidcLoginMutator for configuration

356

*/

357

public static OidcLoginMutator mockOidcLogin();

358

359

/**

360

* Fluent configuration for OIDC login authentication

361

*/

362

public interface OidcLoginMutator extends WebTestClientConfigurer, MockServerConfigurer {

363

/**

364

* Configure ID token

365

* @param idTokenBuilderConsumer Consumer to configure ID token

366

* @return OidcLoginMutator for chaining

367

*/

368

OidcLoginMutator idToken(Consumer<OidcIdToken.Builder> idTokenBuilderConsumer);

369

370

/**

371

* Configure user info

372

* @param userInfoConsumer Consumer to configure user info

373

* @return OidcLoginMutator for chaining

374

*/

375

OidcLoginMutator userInfo(Consumer<Map<String, Object>> userInfoConsumer);

376

377

/**

378

* Set client registration ID

379

* @param clientRegistrationId The client registration ID

380

* @return OidcLoginMutator for chaining

381

*/

382

OidcLoginMutator clientRegistrationId(String clientRegistrationId);

383

384

/**

385

* Set authorities for OIDC authentication

386

* @param authorities The granted authorities

387

* @return OidcLoginMutator for chaining

388

*/

389

OidcLoginMutator authorities(GrantedAuthority... authorities);

390

}

391

```

392

393

**Usage Examples:**

394

395

```java

396

@Test

397

public void testWithOidcLogin() {

398

webTestClient

399

.mutateWith(mockOidcLogin()

400

.clientRegistrationId("auth0")

401

.idToken(token -> token

402

.claim("sub", "user123")

403

.claim("email", "user@example.com")

404

.claim("email_verified", true))

405

.userInfo(userInfo -> {

406

userInfo.put("given_name", "John");

407

userInfo.put("family_name", "Doe");

408

userInfo.put("picture", "https://example.com/avatar.jpg");

409

})

410

.authorities(

411

new SimpleGrantedAuthority("ROLE_USER"),

412

new SimpleGrantedAuthority("SCOPE_openid")

413

))

414

.get().uri("/oidc/userinfo")

415

.exchange()

416

.expectStatus().isOk();

417

}

418

```

419

420

### OAuth2 Client

421

422

OAuth2 client configuration for reactive applications.

423

424

```java { .api }

425

/**

426

* Mock OAuth2 client with default configuration

427

* @return OAuth2ClientMutator for configuration

428

*/

429

public static OAuth2ClientMutator mockOAuth2Client();

430

431

/**

432

* Mock OAuth2 client with specific registration ID

433

* @param registrationId The client registration ID

434

* @return OAuth2ClientMutator for configuration

435

*/

436

public static OAuth2ClientMutator mockOAuth2Client(String registrationId);

437

438

/**

439

* Fluent configuration for OAuth2 client

440

*/

441

public interface OAuth2ClientMutator extends WebTestClientConfigurer, MockServerConfigurer {

442

/**

443

* Set client registration ID

444

* @param registrationId The client registration ID

445

* @return OAuth2ClientMutator for chaining

446

*/

447

OAuth2ClientMutator registrationId(String registrationId);

448

449

/**

450

* Set access token

451

* @param accessToken The access token

452

* @return OAuth2ClientMutator for chaining

453

*/

454

OAuth2ClientMutator accessToken(OAuth2AccessToken accessToken);

455

456

/**

457

* Configure access token using consumer

458

* @param accessTokenConsumer Consumer to configure access token

459

* @return OAuth2ClientMutator for chaining

460

*/

461

OAuth2ClientMutator accessToken(Consumer<OAuth2AccessToken.Builder> accessTokenConsumer);

462

}

463

```

464

465

**Usage Examples:**

466

467

```java

468

@Test

469

public void testWithOAuth2Client() {

470

webTestClient

471

.mutateWith(mockOAuth2Client("api-client")

472

.accessToken(token -> token

473

.tokenType(OAuth2AccessToken.TokenType.BEARER)

474

.scopes(Set.of("read", "write"))

475

.tokenValue("access-token-value")))

476

.get().uri("/api/client-data")

477

.exchange()

478

.expectStatus().isOk();

479

}

480

```

481

482

### CSRF Protection

483

484

CSRF token support for reactive applications.

485

486

```java { .api }

487

/**

488

* Mock CSRF support for reactive applications

489

* @return CsrfMutator for configuration

490

*/

491

public static CsrfMutator csrf();

492

493

/**

494

* Fluent configuration for CSRF in reactive tests

495

*/

496

public interface CsrfMutator extends WebTestClientConfigurer, MockServerConfigurer {

497

// CSRF-specific configuration methods for reactive testing

498

}

499

```

500

501

**Usage Examples:**

502

503

```java

504

@Test

505

public void testCsrfProtectedEndpoint() {

506

webTestClient

507

.mutateWith(csrf())

508

.mutateWith(mockUser())

509

.post().uri("/api/form-submit")

510

.bodyValue("data=test")

511

.exchange()

512

.expectStatus().isOk();

513

}

514

```

515

516

## Common Reactive Testing Patterns

517

518

### Comprehensive Reactive Security Test

519

520

```java

521

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

522

public class ReactiveSecurityIntegrationTest {

523

524

@Autowired

525

private WebTestClient webTestClient;

526

527

@Test

528

public void testUnauthenticatedAccess() {

529

webTestClient

530

.get().uri("/api/public")

531

.exchange()

532

.expectStatus().isOk();

533

534

webTestClient

535

.get().uri("/api/secure")

536

.exchange()

537

.expectStatus().isUnauthorized();

538

}

539

540

@Test

541

public void testUserAuthentication() {

542

webTestClient

543

.mutateWith(mockUser("testuser").roles("USER"))

544

.get().uri("/api/user-data")

545

.exchange()

546

.expectStatus().isOk()

547

.expectBody(String.class)

548

.value(response -> {

549

assertThat(response).contains("testuser");

550

});

551

}

552

553

@Test

554

public void testAdminOnlyEndpoint() {

555

// Test user access - should be forbidden

556

webTestClient

557

.mutateWith(mockUser("user").roles("USER"))

558

.get().uri("/api/admin")

559

.exchange()

560

.expectStatus().isForbidden();

561

562

// Test admin access - should succeed

563

webTestClient

564

.mutateWith(mockUser("admin").roles("ADMIN"))

565

.get().uri("/api/admin")

566

.exchange()

567

.expectStatus().isOk();

568

}

569

570

@Test

571

public void testJwtAuthentication() {

572

webTestClient

573

.mutateWith(mockJwt()

574

.jwt(jwt -> jwt

575

.claim("sub", "jwt-user")

576

.claim("scope", "read write"))

577

.authorities(

578

new SimpleGrantedAuthority("SCOPE_read"),

579

new SimpleGrantedAuthority("SCOPE_write")

580

))

581

.get().uri("/api/jwt-data")

582

.exchange()

583

.expectStatus().isOk();

584

}

585

586

@Test

587

public void testOAuth2Login() {

588

webTestClient

589

.mutateWith(mockOAuth2Login()

590

.attributes(attrs -> {

591

attrs.put("sub", "oauth-user");

592

attrs.put("name", "OAuth User");

593

attrs.put("email", "oauth@example.com");

594

}))

595

.get().uri("/oauth2/profile")

596

.exchange()

597

.expectStatus().isOk()

598

.expectBody()

599

.jsonPath("$.name").isEqualTo("OAuth User")

600

.jsonPath("$.email").isEqualTo("oauth@example.com");

601

}

602

603

@Test

604

public void testReactiveCsrfProtection() {

605

webTestClient

606

.mutateWith(csrf())

607

.mutateWith(mockUser())

608

.post().uri("/api/form")

609

.contentType(MediaType.APPLICATION_FORM_URLENCODED)

610

.bodyValue("field=value")

611

.exchange()

612

.expectStatus().isOk();

613

}

614

}

615

```

616

617

### Testing Reactive Security Configurations

618

619

```java

620

@Test

621

public void testSecurityConfiguration() {

622

// Test method security

623

webTestClient

624

.mutateWith(mockUser("user").roles("USER"))

625

.get().uri("/api/user-method")

626

.exchange()

627

.expectStatus().isOk();

628

629

webTestClient

630

.mutateWith(mockUser("user").roles("USER"))

631

.get().uri("/api/admin-method")

632

.exchange()

633

.expectStatus().isForbidden();

634

635

// Test reactive security context

636

webTestClient

637

.mutateWith(mockUser("testuser"))

638

.get().uri("/api/current-user")

639

.exchange()

640

.expectStatus().isOk()

641

.expectBody(String.class)

642

.isEqualTo("testuser");

643

}

644

```

645

646

### Testing Error Scenarios

647

648

```java

649

@Test

650

public void testAuthenticationFailures() {

651

// Test expired JWT

652

Jwt expiredJwt = Jwt.withTokenValue("expired-token")

653

.header("alg", "HS256")

654

.claim("sub", "user")

655

.claim("exp", Instant.now().minusSeconds(3600)) // Expired

656

.build();

657

658

webTestClient

659

.mutateWith(mockJwt().jwt(expiredJwt))

660

.get().uri("/api/secure")

661

.exchange()

662

.expectStatus().isUnauthorized();

663

664

// Test insufficient authorities

665

webTestClient

666

.mutateWith(mockUser("user").roles("USER"))

667

.get().uri("/api/admin-only")

668

.exchange()

669

.expectStatus().isForbidden();

670

}

671

```