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
```