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

test-context-management.mddocs/

0

# Test Context Management

1

2

Low-level utilities for programmatic security context management and integration with Spring Test framework execution listeners. These utilities provide direct control over security contexts and enable advanced testing scenarios beyond declarative annotations.

3

4

## Capabilities

5

6

### TestSecurityContextHolder

7

8

Core utility class for managing security contexts in test environments, providing thread-local context management optimized for testing scenarios.

9

10

```java { .api }

11

/**

12

* Thread-local SecurityContext management for testing environments

13

* Provides isolation between test methods and threads

14

*/

15

public class TestSecurityContextHolder {

16

17

/**

18

* Set the SecurityContext for the current thread

19

* @param context The SecurityContext to set

20

*/

21

public static void setContext(SecurityContext context);

22

23

/**

24

* Get the current SecurityContext for this thread

25

* @return The current SecurityContext or empty context if none set

26

*/

27

public static SecurityContext getContext();

28

29

/**

30

* Clear the SecurityContext for the current thread

31

* Should be called after each test to prevent context leakage

32

*/

33

public static void clearContext();

34

35

/**

36

* Create and set SecurityContext with the provided Authentication

37

* Convenience method that creates SecurityContext automatically

38

* @param authentication The Authentication to set in a new SecurityContext

39

*/

40

public static void setAuthentication(Authentication authentication);

41

}

42

```

43

44

**Usage Examples:**

45

46

```java

47

@Test

48

public void testWithProgrammaticSecurityContext() {

49

// Create custom authentication

50

Authentication auth = new UsernamePasswordAuthenticationToken(

51

"programmatic-user",

52

"password",

53

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

54

);

55

56

// Set authentication in test context

57

TestSecurityContextHolder.setAuthentication(auth);

58

59

try {

60

// Test code that requires security context

61

SecurityContext context = TestSecurityContextHolder.getContext();

62

assertThat(context.getAuthentication().getName()).isEqualTo("programmatic-user");

63

64

// Test business logic that depends on security context

65

String currentUser = getCurrentUsername(); // Your method

66

assertThat(currentUser).isEqualTo("programmatic-user");

67

68

} finally {

69

// Always clean up

70

TestSecurityContextHolder.clearContext();

71

}

72

}

73

74

@Test

75

public void testWithCustomSecurityContext() {

76

// Create custom SecurityContext

77

SecurityContext customContext = SecurityContextHolder.createEmptyContext();

78

79

// Create complex authentication with custom principal

80

CustomUserPrincipal principal = new CustomUserPrincipal("custom-user", "department-A");

81

Authentication auth = new UsernamePasswordAuthenticationToken(

82

principal,

83

"credentials",

84

Arrays.asList(

85

new SimpleGrantedAuthority("ROLE_USER"),

86

new SimpleGrantedAuthority("DEPT_A")

87

)

88

);

89

90

customContext.setAuthentication(auth);

91

TestSecurityContextHolder.setContext(customContext);

92

93

try {

94

// Test with custom context

95

SecurityContext retrievedContext = TestSecurityContextHolder.getContext();

96

assertThat(retrievedContext).isEqualTo(customContext);

97

98

CustomUserPrincipal retrievedPrincipal =

99

(CustomUserPrincipal) retrievedContext.getAuthentication().getPrincipal();

100

assertThat(retrievedPrincipal.getDepartment()).isEqualTo("department-A");

101

102

} finally {

103

TestSecurityContextHolder.clearContext();

104

}

105

}

106

```

107

108

### TestSecurityContextHolderStrategyAdapter

109

110

Adapter that integrates TestSecurityContextHolder with Spring Security's SecurityContextHolderStrategy interface.

111

112

```java { .api }

113

/**

114

* Adapter that bridges TestSecurityContextHolder to SecurityContextHolderStrategy

115

* Enables TestSecurityContextHolder to work with SecurityContextHolder strategy pattern

116

*/

117

public class TestSecurityContextHolderStrategyAdapter implements SecurityContextHolderStrategy {

118

119

/**

120

* Clear the SecurityContext using TestSecurityContextHolder

121

*/

122

void clearContext();

123

124

/**

125

* Get SecurityContext using TestSecurityContextHolder

126

* @return The current SecurityContext

127

*/

128

SecurityContext getContext();

129

130

/**

131

* Set SecurityContext using TestSecurityContextHolder

132

* @param context The SecurityContext to set

133

*/

134

void setContext(SecurityContext context);

135

136

/**

137

* Create empty SecurityContext

138

* @return New empty SecurityContext instance

139

*/

140

SecurityContext createEmptyContext();

141

}

142

```

143

144

**Usage Examples:**

145

146

```java

147

@Test

148

public void testWithStrategyAdapter() {

149

TestSecurityContextHolderStrategyAdapter strategy =

150

new TestSecurityContextHolderStrategyAdapter();

151

152

// Use strategy pattern

153

SecurityContext context = strategy.createEmptyContext();

154

Authentication auth = new UsernamePasswordAuthenticationToken(

155

"strategy-user", "password",

156

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

157

);

158

context.setAuthentication(auth);

159

160

strategy.setContext(context);

161

162

try {

163

SecurityContext retrievedContext = strategy.getContext();

164

assertThat(retrievedContext.getAuthentication().getName())

165

.isEqualTo("strategy-user");

166

} finally {

167

strategy.clearContext();

168

}

169

}

170

```

171

172

## Test Execution Listeners

173

174

### WithSecurityContextTestExecutionListener

175

176

TestExecutionListener that processes `@WithSecurityContext` meta-annotations and manages security context lifecycle during test execution.

177

178

```java { .api }

179

/**

180

* TestExecutionListener that processes @WithSecurityContext annotations

181

* Handles security context setup and cleanup during test execution

182

* Order: 10000 (runs after most Spring TestExecutionListeners)

183

*/

184

public class WithSecurityContextTestExecutionListener implements TestExecutionListener {

185

186

/**

187

* Process @WithSecurityContext annotations before test method execution

188

* Sets up SecurityContext based on annotation configuration

189

*/

190

void beforeTestMethod(TestContext testContext) throws Exception;

191

192

/**

193

* Process @WithSecurityContext annotations before test execution

194

* Alternative timing based on TestExecutionEvent configuration

195

*/

196

void beforeTestExecution(TestContext testContext) throws Exception;

197

198

/**

199

* Clean up SecurityContext after test method completion

200

* Ensures no context leakage between tests

201

*/

202

void afterTestMethod(TestContext testContext) throws Exception;

203

204

/**

205

* Clean up SecurityContext after test execution

206

* Alternative cleanup timing based on configuration

207

*/

208

void afterTestExecution(TestContext testContext) throws Exception;

209

}

210

```

211

212

### ReactorContextTestExecutionListener

213

214

TestExecutionListener that sets up Reactor Context with SecurityContext for reactive testing scenarios.

215

216

```java { .api }

217

/**

218

* TestExecutionListener for reactive security context management

219

* Integrates SecurityContext with Reactor Context for WebFlux testing

220

* Order: 11000 (runs after WithSecurityContextTestExecutionListener)

221

*/

222

public class ReactorContextTestExecutionListener implements TestExecutionListener {

223

224

/**

225

* Set up Reactor Context with SecurityContext before test execution

226

* Enables reactive components to access security context

227

*/

228

void beforeTestExecution(TestContext testContext) throws Exception;

229

230

/**

231

* Clean up Reactor Context after test execution

232

* Prevents context pollution between tests

233

*/

234

void afterTestExecution(TestContext testContext) throws Exception;

235

}

236

```

237

238

### DelegatingTestExecutionListener

239

240

Utility listener that delegates to other TestExecutionListeners based on configuration.

241

242

```java { .api }

243

/**

244

* TestExecutionListener that delegates to other listeners

245

* Useful for conditional or dynamic listener management

246

*/

247

public class DelegatingTestExecutionListener implements TestExecutionListener {

248

249

/**

250

* Constructor with delegate listeners

251

* @param delegates The TestExecutionListeners to delegate to

252

*/

253

public DelegatingTestExecutionListener(TestExecutionListener... delegates);

254

255

// Delegates all TestExecutionListener methods to configured delegates

256

}

257

```

258

259

## Web Testing Utilities

260

261

### WebTestUtils

262

263

Utility class providing low-level access to security infrastructure components in servlet environments, useful for advanced testing scenarios that require direct manipulation of security filters.

264

265

```java { .api }

266

/**

267

* Utility class for accessing and manipulating Spring Security infrastructure

268

* components during testing. Provides access to SecurityContextRepository,

269

* CsrfTokenRepository, and related components from HttpServletRequest.

270

*/

271

public abstract class WebTestUtils {

272

273

/**

274

* Get SecurityContextRepository from request's filter chain

275

* @param request The HttpServletRequest to examine

276

* @return SecurityContextRepository or default HttpSessionSecurityContextRepository

277

*/

278

public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request);

279

280

/**

281

* Set SecurityContextRepository for request's filter chain

282

* @param request The HttpServletRequest to modify

283

* @param securityContextRepository The SecurityContextRepository to set

284

*/

285

public static void setSecurityContextRepository(

286

HttpServletRequest request,

287

SecurityContextRepository securityContextRepository

288

);

289

290

/**

291

* Get CsrfTokenRepository from request's filter chain

292

* @param request The HttpServletRequest to examine

293

* @return CsrfTokenRepository or default HttpSessionCsrfTokenRepository

294

*/

295

public static CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request);

296

297

/**

298

* Get CsrfTokenRequestHandler from request's filter chain

299

* @param request The HttpServletRequest to examine

300

* @return CsrfTokenRequestHandler or default XorCsrfTokenRequestAttributeHandler

301

*/

302

public static CsrfTokenRequestHandler getCsrfTokenRequestHandler(HttpServletRequest request);

303

304

/**

305

* Set CsrfTokenRepository for request's filter chain

306

* @param request The HttpServletRequest to modify

307

* @param repository The CsrfTokenRepository to set

308

*/

309

public static void setCsrfTokenRepository(

310

HttpServletRequest request,

311

CsrfTokenRepository repository

312

);

313

}

314

```

315

316

**Usage Examples:**

317

318

```java

319

@SpringBootTest

320

@AutoConfigureTestDatabase

321

public class WebInfrastructureTest {

322

323

@Autowired

324

private WebApplicationContext context;

325

326

private MockMvc mockMvc;

327

328

@BeforeEach

329

public void setup() {

330

mockMvc = MockMvcBuilders

331

.webAppContextSetup(context)

332

.apply(springSecurity())

333

.build();

334

}

335

336

@Test

337

public void testCustomSecurityContextRepository() throws Exception {

338

// Create custom SecurityContextRepository for testing

339

SecurityContextRepository customRepo = new HttpSessionSecurityContextRepository();

340

341

mockMvc.perform(get("/test-endpoint")

342

.requestAttr("customRepo", customRepo)

343

.with(user("testuser")))

344

.andDo(result -> {

345

HttpServletRequest request = result.getRequest();

346

347

// Get current repository

348

SecurityContextRepository currentRepo =

349

WebTestUtils.getSecurityContextRepository(request);

350

assertThat(currentRepo).isNotNull();

351

352

// Set custom repository

353

WebTestUtils.setSecurityContextRepository(request, customRepo);

354

355

// Verify change

356

SecurityContextRepository updatedRepo =

357

WebTestUtils.getSecurityContextRepository(request);

358

assertThat(updatedRepo).isEqualTo(customRepo);

359

})

360

.andExpect(status().isOk());

361

}

362

363

@Test

364

public void testCsrfTokenRepositoryAccess() throws Exception {

365

mockMvc.perform(post("/csrf-endpoint")

366

.with(csrf())

367

.with(user("testuser")))

368

.andDo(result -> {

369

HttpServletRequest request = result.getRequest();

370

371

// Access CSRF token repository

372

CsrfTokenRepository tokenRepo =

373

WebTestUtils.getCsrfTokenRepository(request);

374

assertThat(tokenRepo).isNotNull();

375

376

// Access CSRF token request handler

377

CsrfTokenRequestHandler requestHandler =

378

WebTestUtils.getCsrfTokenRequestHandler(request);

379

assertThat(requestHandler).isNotNull();

380

})

381

.andExpect(status().isOk());

382

}

383

384

@Test

385

public void testCustomCsrfConfiguration() throws Exception {

386

// Create custom CSRF token repository

387

CsrfTokenRepository customCsrfRepo = new HttpSessionCsrfTokenRepository();

388

389

mockMvc.perform(post("/custom-csrf")

390

.with(csrf())

391

.with(user("testuser")))

392

.andDo(result -> {

393

HttpServletRequest request = result.getRequest();

394

395

// Set custom CSRF repository

396

WebTestUtils.setCsrfTokenRepository(request, customCsrfRepo);

397

398

// Verify the change

399

CsrfTokenRepository retrievedRepo =

400

WebTestUtils.getCsrfTokenRepository(request);

401

assertThat(retrievedRepo).isEqualTo(customCsrfRepo);

402

})

403

.andExpect(status().isOk());

404

}

405

}

406

```

407

408

## Advanced Testing Patterns

409

410

### Manual Context Management in Complex Tests

411

412

```java

413

@SpringBootTest

414

public class ComplexSecurityContextTest {

415

416

@Test

417

public void testMultipleSecurityContexts() {

418

// First context - regular user

419

Authentication userAuth = new UsernamePasswordAuthenticationToken(

420

"user", "password",

421

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

422

);

423

TestSecurityContextHolder.setAuthentication(userAuth);

424

425

try {

426

// Test user operations

427

String userResult = performUserOperation();

428

assertThat(userResult).contains("user");

429

430

// Switch to admin context

431

Authentication adminAuth = new UsernamePasswordAuthenticationToken(

432

"admin", "password",

433

Arrays.asList(

434

new SimpleGrantedAuthority("ROLE_ADMIN"),

435

new SimpleGrantedAuthority("ROLE_USER")

436

)

437

);

438

TestSecurityContextHolder.setAuthentication(adminAuth);

439

440

// Test admin operations

441

String adminResult = performAdminOperation();

442

assertThat(adminResult).contains("admin");

443

444

} finally {

445

TestSecurityContextHolder.clearContext();

446

}

447

}

448

449

@Test

450

public void testSecurityContextInCallbacks() {

451

// Set up context for async operations

452

Authentication auth = new UsernamePasswordAuthenticationToken(

453

"async-user", "password",

454

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

455

);

456

TestSecurityContextHolder.setAuthentication(auth);

457

458

try {

459

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {

460

// Context should be available in async callback

461

SecurityContext context = TestSecurityContextHolder.getContext();

462

return context.getAuthentication().getName();

463

});

464

465

String result = future.get(1, TimeUnit.SECONDS);

466

assertThat(result).isEqualTo("async-user");

467

468

} catch (Exception e) {

469

fail("Async operation failed", e);

470

} finally {

471

TestSecurityContextHolder.clearContext();

472

}

473

}

474

}

475

```

476

477

### Custom Test Execution Listener

478

479

```java

480

/**

481

* Custom TestExecutionListener for specialized security context management

482

*/

483

public class CustomSecurityTestExecutionListener implements TestExecutionListener {

484

485

@Override

486

public void beforeTestMethod(TestContext testContext) throws Exception {

487

Method testMethod = testContext.getTestMethod();

488

489

// Check for custom security annotation

490

CustomSecurity customSecurity = testMethod.getAnnotation(CustomSecurity.class);

491

if (customSecurity != null) {

492

setupCustomSecurityContext(customSecurity);

493

}

494

}

495

496

@Override

497

public void afterTestMethod(TestContext testContext) throws Exception {

498

// Always clean up

499

TestSecurityContextHolder.clearContext();

500

}

501

502

private void setupCustomSecurityContext(CustomSecurity annotation) {

503

// Create custom authentication based on annotation

504

Authentication auth = createCustomAuthentication(

505

annotation.username(),

506

annotation.permissions()

507

);

508

TestSecurityContextHolder.setAuthentication(auth);

509

}

510

511

private Authentication createCustomAuthentication(String username, String[] permissions) {

512

List<GrantedAuthority> authorities = Arrays.stream(permissions)

513

.map(SimpleGrantedAuthority::new)

514

.collect(Collectors.toList());

515

516

return new UsernamePasswordAuthenticationToken(username, "password", authorities);

517

}

518

}

519

520

// Usage with custom annotation

521

@Target(ElementType.METHOD)

522

@Retention(RetentionPolicy.RUNTIME)

523

public @interface CustomSecurity {

524

String username() default "testuser";

525

String[] permissions() default {};

526

}

527

528

// Test class using custom listener

529

@SpringBootTest

530

@TestExecutionListeners(

531

listeners = CustomSecurityTestExecutionListener.class,

532

mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS

533

)

534

public class CustomSecurityTest {

535

536

@Test

537

@CustomSecurity(username = "custom-user", permissions = {"READ", "WRITE"})

538

public void testWithCustomSecurity() {

539

SecurityContext context = TestSecurityContextHolder.getContext();

540

assertThat(context.getAuthentication().getName()).isEqualTo("custom-user");

541

542

Collection<? extends GrantedAuthority> authorities =

543

context.getAuthentication().getAuthorities();

544

assertThat(authorities).hasSize(2);

545

assertThat(authorities.stream().map(GrantedAuthority::getAuthority))

546

.containsExactlyInAnyOrder("READ", "WRITE");

547

}

548

}

549

```

550

551

### Integration with MockMvc and WebTestClient

552

553

```java

554

@SpringBootTest

555

public class IntegratedContextManagementTest {

556

557

@Autowired

558

private WebApplicationContext context;

559

560

private MockMvc mockMvc;

561

562

@BeforeEach

563

public void setup() {

564

mockMvc = MockMvcBuilders

565

.webAppContextSetup(context)

566

.apply(springSecurity())

567

.build();

568

}

569

570

@Test

571

public void testMixedContextUsage() throws Exception {

572

// Set up context programmatically

573

Authentication auth = new UsernamePasswordAuthenticationToken(

574

"mixed-user", "password",

575

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

576

);

577

TestSecurityContextHolder.setAuthentication(auth);

578

579

try {

580

// Use with MockMvc via testSecurityContext()

581

mockMvc.perform(get("/user-info")

582

.with(testSecurityContext()))

583

.andExpect(status().isOk())

584

.andExpect(authenticated().withUsername("mixed-user"));

585

586

// Verify context is still available

587

SecurityContext retrievedContext = TestSecurityContextHolder.getContext();

588

assertThat(retrievedContext.getAuthentication().getName())

589

.isEqualTo("mixed-user");

590

591

} finally {

592

TestSecurityContextHolder.clearContext();

593

}

594

}

595

}

596

```

597

598

### Thread Safety and Isolation

599

600

```java

601

@SpringBootTest

602

public class ThreadSafetyTest {

603

604

@Test

605

public void testContextIsolationBetweenThreads() throws Exception {

606

// Main thread context

607

TestSecurityContextHolder.setAuthentication(

608

new UsernamePasswordAuthenticationToken("main-user", "password",

609

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

610

);

611

612

try {

613

// Verify main thread context

614

assertThat(TestSecurityContextHolder.getContext().getAuthentication().getName())

615

.isEqualTo("main-user");

616

617

// Test in separate thread

618

CompletableFuture<String> otherThreadResult = CompletableFuture.supplyAsync(() -> {

619

// Other thread should have empty context

620

SecurityContext otherContext = TestSecurityContextHolder.getContext();

621

return otherContext.getAuthentication() != null ?

622

otherContext.getAuthentication().getName() : "no-auth";

623

});

624

625

String result = otherThreadResult.get(1, TimeUnit.SECONDS);

626

assertThat(result).isEqualTo("no-auth");

627

628

// Main thread context should still be intact

629

assertThat(TestSecurityContextHolder.getContext().getAuthentication().getName())

630

.isEqualTo("main-user");

631

632

} finally {

633

TestSecurityContextHolder.clearContext();

634

}

635

}

636

}

637

```