or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdfilter-handling.mdindex.mdlisteners.mdmonitoring.mdservlet-context.mdservlet-handling.mdtesting.md

testing.mddocs/

0

# Testing Utilities

1

2

Eclipse Jetty's servlet testing utilities provide comprehensive support for testing servlets, filters, and web applications without requiring a full server deployment through the ServletTester class and related testing infrastructure.

3

4

## ServletTester

5

6

> **Note**: ServletTester may be deprecated and eventually removed in future Jetty versions. For new projects, consider using the combination of `Server` + `LocalConnector` + `ServletContextHandler` for servlet testing instead.

7

8

The `ServletTester` provides a lightweight testing environment for servlet-based applications.

9

10

```java { .api }

11

public class ServletTester extends ContainerLifeCycle {

12

// Constructors

13

public ServletTester();

14

public ServletTester(String contextPath);

15

public ServletTester(String contextPath, int options);

16

17

// Context management

18

public ServletContextHandler getContext();

19

public void setContextPath(String contextPath);

20

public String getContextPath();

21

22

// Servlet registration

23

public void addServlet(Class<? extends Servlet> servlet, String pathSpec);

24

public ServletHolder addServlet(String className, String pathSpec);

25

26

// Filter registration

27

public void addFilter(Class<? extends Filter> filter, String pathSpec,

28

EnumSet<DispatcherType> dispatches);

29

public FilterHolder addFilter(String className, String pathSpec,

30

EnumSet<DispatcherType> dispatches);

31

32

// Request execution

33

public String getResponses(String request);

34

public String getResponses(String request, long idleFor, TimeUnit units);

35

}

36

```

37

38

## Usage Examples

39

40

### Basic Servlet Testing

41

42

```java

43

import org.eclipse.jetty.servlet.ServletTester;

44

import org.eclipse.jetty.servlet.ServletHolder;

45

import jakarta.servlet.http.HttpServlet;

46

import jakarta.servlet.http.HttpServletRequest;

47

import jakarta.servlet.http.HttpServletResponse;

48

import java.io.IOException;

49

50

// Test servlet implementation

51

public class TestServlet extends HttpServlet {

52

@Override

53

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

54

throws IOException {

55

resp.setContentType("text/plain");

56

resp.getWriter().println("Hello from test servlet!");

57

}

58

59

@Override

60

protected void doPost(HttpServletRequest req, HttpServletResponse resp)

61

throws IOException {

62

String data = req.getParameter("data");

63

resp.setContentType("application/json");

64

resp.getWriter().println("{\"received\":\"" + data + "\"}");

65

}

66

}

67

68

// Basic servlet test

69

public class BasicServletTest {

70

71

@Test

72

public void testSimpleGetRequest() throws Exception {

73

// Create servlet tester

74

ServletTester tester = new ServletTester();

75

tester.setContextPath("/test");

76

77

// Add servlet

78

tester.addServlet(TestServlet.class, "/hello");

79

80

// Start tester

81

tester.start();

82

83

try {

84

// Create HTTP request

85

String request = "GET /test/hello HTTP/1.1\r\n" +

86

"Host: localhost\r\n" +

87

"Connection: close\r\n" +

88

"\r\n";

89

90

// Execute request and get response

91

String response = tester.getResponses(request);

92

93

// Verify response

94

assertTrue(response.contains("HTTP/1.1 200 OK"));

95

assertTrue(response.contains("Hello from test servlet!"));

96

97

} finally {

98

tester.stop();

99

}

100

}

101

102

@Test

103

public void testPostRequest() throws Exception {

104

ServletTester tester = new ServletTester("/api");

105

tester.addServlet(TestServlet.class, "/data");

106

tester.start();

107

108

try {

109

String requestBody = "data=testvalue";

110

String request = "POST /api/data HTTP/1.1\r\n" +

111

"Host: localhost\r\n" +

112

"Content-Type: application/x-www-form-urlencoded\r\n" +

113

"Content-Length: " + requestBody.length() + "\r\n" +

114

"Connection: close\r\n" +

115

"\r\n" +

116

requestBody;

117

118

String response = tester.getResponses(request);

119

120

assertTrue(response.contains("HTTP/1.1 200 OK"));

121

assertTrue(response.contains("{\"received\":\"testvalue\"}"));

122

123

} finally {

124

tester.stop();

125

}

126

}

127

}

128

```

129

130

### Filter Testing

131

132

```java

133

import jakarta.servlet.Filter;

134

import jakarta.servlet.FilterChain;

135

import jakarta.servlet.FilterConfig;

136

import jakarta.servlet.ServletRequest;

137

import jakarta.servlet.ServletResponse;

138

import jakarta.servlet.http.HttpServletRequest;

139

import jakarta.servlet.http.HttpServletResponse;

140

import jakarta.servlet.DispatcherType;

141

import java.util.EnumSet;

142

143

// Test filter implementation

144

public class LoggingFilter implements Filter {

145

146

@Override

147

public void init(FilterConfig filterConfig) {

148

// Initialize filter

149

}

150

151

@Override

152

public void doFilter(ServletRequest request, ServletResponse response,

153

FilterChain chain) throws IOException, ServletException {

154

155

HttpServletRequest httpReq = (HttpServletRequest) request;

156

HttpServletResponse httpResp = (HttpServletResponse) response;

157

158

// Add request ID header

159

String requestId = "REQ-" + System.currentTimeMillis();

160

httpResp.setHeader("X-Request-ID", requestId);

161

162

// Log request

163

System.out.println("Processing request: " + httpReq.getRequestURI() +

164

" [" + requestId + "]");

165

166

// Continue chain

167

chain.doFilter(request, response);

168

169

// Log response

170

System.out.println("Response status: " + httpResp.getStatus() +

171

" [" + requestId + "]");

172

}

173

174

@Override

175

public void destroy() {

176

// Cleanup filter

177

}

178

}

179

180

// Filter test

181

public class FilterTest {

182

183

@Test

184

public void testFilterChain() throws Exception {

185

ServletTester tester = new ServletTester();

186

187

// Add filter

188

tester.addFilter(LoggingFilter.class, "/*",

189

EnumSet.of(DispatcherType.REQUEST));

190

191

// Add servlet

192

tester.addServlet(TestServlet.class, "/test");

193

194

tester.start();

195

196

try {

197

String request = "GET /test HTTP/1.1\r\n" +

198

"Host: localhost\r\n" +

199

"Connection: close\r\n" +

200

"\r\n";

201

202

String response = tester.getResponses(request);

203

204

// Verify filter added header

205

assertTrue(response.contains("X-Request-ID: REQ-"));

206

assertTrue(response.contains("Hello from test servlet!"));

207

208

} finally {

209

tester.stop();

210

}

211

}

212

}

213

```

214

215

### Complex Application Testing

216

217

```java

218

// Complex servlet with dependencies

219

public class DataServlet extends HttpServlet {

220

private DatabaseService dbService;

221

222

@Override

223

public void init() throws ServletException {

224

super.init();

225

// Initialize database service (mock for testing)

226

String dbUrl = getInitParameter("database.url");

227

this.dbService = new MockDatabaseService(dbUrl);

228

}

229

230

@Override

231

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

232

throws IOException {

233

String id = req.getParameter("id");

234

235

if (id == null) {

236

resp.sendError(400, "Missing id parameter");

237

return;

238

}

239

240

try {

241

String data = dbService.getData(id);

242

if (data == null) {

243

resp.sendError(404, "Data not found");

244

return;

245

}

246

247

resp.setContentType("application/json");

248

resp.getWriter().println("{\"id\":\"" + id + "\",\"data\":\"" + data + "\"}");

249

250

} catch (Exception e) {

251

resp.sendError(500, "Database error: " + e.getMessage());

252

}

253

}

254

255

@Override

256

protected void doPost(HttpServletRequest req, HttpServletResponse resp)

257

throws IOException {

258

String data = req.getParameter("data");

259

260

if (data == null || data.trim().isEmpty()) {

261

resp.sendError(400, "Missing data parameter");

262

return;

263

}

264

265

try {

266

String id = dbService.saveData(data);

267

resp.setContentType("application/json");

268

resp.getWriter().println("{\"id\":\"" + id + "\",\"status\":\"saved\"}");

269

270

} catch (Exception e) {

271

resp.sendError(500, "Failed to save data: " + e.getMessage());

272

}

273

}

274

275

@Override

276

public void destroy() {

277

if (dbService != null) {

278

dbService.close();

279

}

280

super.destroy();

281

}

282

}

283

284

// Mock database service for testing

285

public class MockDatabaseService {

286

private final Map<String, String> data = new HashMap<>();

287

private int nextId = 1;

288

289

public MockDatabaseService(String url) {

290

// Initialize with test data

291

data.put("1", "Test data 1");

292

data.put("2", "Test data 2");

293

}

294

295

public String getData(String id) {

296

return data.get(id);

297

}

298

299

public String saveData(String value) {

300

String id = String.valueOf(nextId++);

301

data.put(id, value);

302

return id;

303

}

304

305

public void close() {

306

data.clear();

307

}

308

}

309

310

// Complex application test

311

public class ComplexApplicationTest {

312

313

@Test

314

public void testDataRetrieval() throws Exception {

315

ServletTester tester = new ServletTester("/api");

316

317

// Configure servlet with init parameters

318

ServletHolder dataServlet = tester.addServlet(DataServlet.class.getName(), "/data");

319

dataServlet.setInitParameter("database.url", "mock://localhost");

320

321

tester.start();

322

323

try {

324

// Test successful data retrieval

325

String request = "GET /api/data?id=1 HTTP/1.1\r\n" +

326

"Host: localhost\r\n" +

327

"Connection: close\r\n" +

328

"\r\n";

329

330

String response = tester.getResponses(request);

331

332

assertTrue(response.contains("HTTP/1.1 200 OK"));

333

assertTrue(response.contains("{\"id\":\"1\",\"data\":\"Test data 1\"}"));

334

335

// Test missing data

336

request = "GET /api/data?id=999 HTTP/1.1\r\n" +

337

"Host: localhost\r\n" +

338

"Connection: close\r\n" +

339

"\r\n";

340

341

response = tester.getResponses(request);

342

assertTrue(response.contains("HTTP/1.1 404"));

343

344

// Test missing parameter

345

request = "GET /api/data HTTP/1.1\r\n" +

346

"Host: localhost\r\n" +

347

"Connection: close\r\n" +

348

"\r\n";

349

350

response = tester.getResponses(request);

351

assertTrue(response.contains("HTTP/1.1 400"));

352

353

} finally {

354

tester.stop();

355

}

356

}

357

358

@Test

359

public void testDataSaving() throws Exception {

360

ServletTester tester = new ServletTester("/api");

361

362

ServletHolder dataServlet = tester.addServlet(DataServlet.class.getName(), "/data");

363

dataServlet.setInitParameter("database.url", "mock://localhost");

364

365

tester.start();

366

367

try {

368

String requestBody = "data=New test data";

369

String request = "POST /api/data HTTP/1.1\r\n" +

370

"Host: localhost\r\n" +

371

"Content-Type: application/x-www-form-urlencoded\r\n" +

372

"Content-Length: " + requestBody.length() + "\r\n" +

373

"Connection: close\r\n" +

374

"\r\n" +

375

requestBody;

376

377

String response = tester.getResponses(request);

378

379

assertTrue(response.contains("HTTP/1.1 200 OK"));

380

assertTrue(response.contains("\"status\":\"saved\""));

381

382

} finally {

383

tester.stop();

384

}

385

}

386

}

387

```

388

389

### Session Testing

390

391

```java

392

// Session-aware servlet

393

public class SessionServlet extends HttpServlet {

394

395

@Override

396

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

397

throws IOException {

398

HttpSession session = req.getSession();

399

400

Integer count = (Integer) session.getAttribute("count");

401

if (count == null) {

402

count = 0;

403

}

404

count++;

405

session.setAttribute("count", count);

406

407

resp.setContentType("text/plain");

408

resp.getWriter().println("Visit count: " + count);

409

resp.getWriter().println("Session ID: " + session.getId());

410

}

411

}

412

413

// Session test

414

public class SessionTest {

415

416

@Test

417

public void testSessionHandling() throws Exception {

418

ServletTester tester = new ServletTester();

419

420

// Enable sessions in the context

421

tester.getContext().setSessionHandler(new SessionHandler());

422

tester.addServlet(SessionServlet.class, "/session");

423

424

tester.start();

425

426

try {

427

// First request - should create session

428

String request1 = "GET /session HTTP/1.1\r\n" +

429

"Host: localhost\r\n" +

430

"Connection: close\r\n" +

431

"\r\n";

432

433

String response1 = tester.getResponses(request1);

434

assertTrue(response1.contains("Visit count: 1"));

435

436

// Extract session cookie

437

String sessionCookie = extractSessionCookie(response1);

438

assertNotNull(sessionCookie);

439

440

// Second request with session cookie

441

String request2 = "GET /session HTTP/1.1\r\n" +

442

"Host: localhost\r\n" +

443

"Cookie: " + sessionCookie + "\r\n" +

444

"Connection: close\r\n" +

445

"\r\n";

446

447

String response2 = tester.getResponses(request2);

448

assertTrue(response2.contains("Visit count: 2"));

449

450

} finally {

451

tester.stop();

452

}

453

}

454

455

private String extractSessionCookie(String response) {

456

String[] lines = response.split("\r\n");

457

for (String line : lines) {

458

if (line.startsWith("Set-Cookie: JSESSIONID=")) {

459

return line.substring("Set-Cookie: ".length());

460

}

461

}

462

return null;

463

}

464

}

465

```

466

467

### Async Servlet Testing

468

469

```java

470

import jakarta.servlet.AsyncContext;

471

import jakarta.servlet.ServletException;

472

import java.util.concurrent.CompletableFuture;

473

474

// Async servlet implementation

475

public class AsyncServlet extends HttpServlet {

476

477

@Override

478

protected void doGet(HttpServletRequest req, HttpServletResponse resp)

479

throws ServletException, IOException {

480

481

// Start async processing

482

AsyncContext asyncContext = req.startAsync();

483

asyncContext.setTimeout(5000); // 5 second timeout

484

485

// Process request asynchronously

486

CompletableFuture.runAsync(() -> {

487

try {

488

// Simulate long-running operation

489

Thread.sleep(1000);

490

491

HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();

492

response.setContentType("text/plain");

493

response.getWriter().println("Async operation completed");

494

495

asyncContext.complete();

496

497

} catch (Exception e) {

498

asyncContext.complete();

499

}

500

});

501

}

502

}

503

504

// Async servlet test

505

public class AsyncServletTest {

506

507

@Test

508

public void testAsyncProcessing() throws Exception {

509

ServletTester tester = new ServletTester();

510

511

// Configure servlet with async support

512

ServletHolder asyncServlet = tester.addServlet(AsyncServlet.class.getName(), "/async");

513

asyncServlet.setAsyncSupported(true);

514

515

tester.start();

516

517

try {

518

String request = "GET /async HTTP/1.1\r\n" +

519

"Host: localhost\r\n" +

520

"Connection: close\r\n" +

521

"\r\n";

522

523

// Use timeout for async response

524

String response = tester.getResponses(request, 10, TimeUnit.SECONDS);

525

526

assertTrue(response.contains("HTTP/1.1 200 OK"));

527

assertTrue(response.contains("Async operation completed"));

528

529

} finally {

530

tester.stop();

531

}

532

}

533

}

534

```

535

536

### Integration Test Framework

537

538

```java

539

// Base test class for servlet integration tests

540

public abstract class ServletIntegrationTestBase {

541

protected ServletTester tester;

542

543

@Before

544

public void setUp() throws Exception {

545

tester = createServletTester();

546

configureServletTester(tester);

547

tester.start();

548

}

549

550

@After

551

public void tearDown() throws Exception {

552

if (tester != null) {

553

tester.stop();

554

}

555

}

556

557

protected ServletTester createServletTester() {

558

return new ServletTester("/test");

559

}

560

561

protected abstract void configureServletTester(ServletTester tester);

562

563

// Utility methods for common test operations

564

protected String sendGetRequest(String path) throws Exception {

565

return sendRequest("GET", path, null, null);

566

}

567

568

protected String sendPostRequest(String path, String body) throws Exception {

569

return sendRequest("POST", path, "application/x-www-form-urlencoded", body);

570

}

571

572

protected String sendJsonRequest(String method, String path, String jsonBody) throws Exception {

573

return sendRequest(method, path, "application/json", jsonBody);

574

}

575

576

protected String sendRequest(String method, String path, String contentType, String body)

577

throws Exception {

578

StringBuilder request = new StringBuilder();

579

request.append(method).append(" ").append(tester.getContextPath()).append(path)

580

.append(" HTTP/1.1\r\n");

581

request.append("Host: localhost\r\n");

582

583

if (contentType != null) {

584

request.append("Content-Type: ").append(contentType).append("\r\n");

585

}

586

587

if (body != null) {

588

request.append("Content-Length: ").append(body.length()).append("\r\n");

589

}

590

591

request.append("Connection: close\r\n");

592

request.append("\r\n");

593

594

if (body != null) {

595

request.append(body);

596

}

597

598

return tester.getResponses(request.toString());

599

}

600

601

// Response parsing utilities

602

protected int getResponseStatus(String response) {

603

String statusLine = response.split("\r\n")[0];

604

String[] parts = statusLine.split(" ");

605

return Integer.parseInt(parts[1]);

606

}

607

608

protected String getResponseBody(String response) {

609

int bodyStart = response.indexOf("\r\n\r\n");

610

if (bodyStart == -1) return "";

611

return response.substring(bodyStart + 4);

612

}

613

614

protected String getResponseHeader(String response, String headerName) {

615

String[] lines = response.split("\r\n");

616

for (String line : lines) {

617

if (line.toLowerCase().startsWith(headerName.toLowerCase() + ":")) {

618

return line.substring(headerName.length() + 1).trim();

619

}

620

}

621

return null;

622

}

623

}

624

625

// Example usage of integration test framework

626

public class ApiServletIntegrationTest extends ServletIntegrationTestBase {

627

628

@Override

629

protected void configureServletTester(ServletTester tester) {

630

// Add authentication filter

631

tester.addFilter(AuthenticationFilter.class, "/api/*",

632

EnumSet.of(DispatcherType.REQUEST));

633

634

// Add API servlets

635

ServletHolder userServlet = tester.addServlet(UserServlet.class.getName(), "/api/users/*");

636

userServlet.setInitParameter("database.url", "mock://test");

637

638

ServletHolder dataServlet = tester.addServlet(DataServlet.class.getName(), "/api/data/*");

639

dataServlet.setInitParameter("cache.enabled", "false");

640

}

641

642

@Test

643

public void testUserApiEndpoints() throws Exception {

644

// Test user creation

645

String createRequest = "{\"name\":\"John Doe\",\"email\":\"john@example.com\"}";

646

String response = sendJsonRequest("POST", "/api/users", createRequest);

647

648

assertEquals(201, getResponseStatus(response));

649

assertTrue(getResponseBody(response).contains("\"id\":"));

650

651

// Test user retrieval

652

response = sendGetRequest("/api/users/1");

653

assertEquals(200, getResponseStatus(response));

654

assertTrue(getResponseBody(response).contains("John Doe"));

655

}

656

}

657

```