• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10124 relevant lines covered (81.44%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.86
/src/main/java/com/meterware/servletunit/JUnitServlet.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.servletunit;
9

10
import jakarta.servlet.ServletException;
11
import jakarta.servlet.http.HttpServlet;
12
import jakarta.servlet.http.HttpServletRequest;
13
import jakarta.servlet.http.HttpServletResponse;
14

15
import java.io.IOException;
16
import java.io.PrintWriter;
17
import java.util.Enumeration;
18

19
import junit.framework.AssertionFailedError;
20
import junit.framework.Test;
21
import junit.framework.TestFailure;
22
import junit.framework.TestResult;
23
import junit.runner.BaseTestRunner;
24

25
/**
26
 * A servlet which can run unit tests inside a servlet context. It may be extended to provide InvocationContext-access
27
 * to such tests if a container-specific implementation of InvocationContextFactory is provided. Combined with
28
 * ServletTestCase, this would permit in-container tests of servlets in a fashion similar to that supported by
29
 * ServletUnit.
30
 **/
31
public class JUnitServlet extends HttpServlet {
32

33
    /** The Constant serialVersionUID. */
34
    private static final long serialVersionUID = 1L;
35

36
    /**
37
     * Instantiates a new j unit servlet.
38
     */
39
    public JUnitServlet() {
×
40
    }
×
41

42
    /**
43
     * Instantiates a new j unit servlet.
44
     *
45
     * @param factory
46
     *            the factory
47
     */
48
    protected JUnitServlet(InvocationContextFactory factory) {
1✔
49
        _factory = factory;
1✔
50
    }
1✔
51

52
    @Override
53
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
54
            throws ServletException, IOException {
55
        ResultsFormatter formatter = getResultsFormatter(request.getParameter("format"));
1✔
56
        response.setContentType(formatter.getContentType());
1✔
57
        final String testName = request.getParameter("test");
1✔
58
        if (testName == null || testName.isEmpty()) {
1!
59
            reportCannotRunTest(response.getWriter(), "No test class specified");
1✔
60
        } else {
61
            ServletTestRunner runner = new ServletTestRunner(response.getWriter(), formatter);
1✔
62
            runner.runTestSuite(testName);
1✔
63
        }
64
        response.getWriter().close();
1✔
65
    }
1✔
66

67
    /**
68
     * Gets the results formatter.
69
     *
70
     * @param formatterName
71
     *            the formatter name
72
     *
73
     * @return the results formatter
74
     */
75
    private ResultsFormatter getResultsFormatter(String formatterName) {
76
        if ("text".equalsIgnoreCase(formatterName)) {
1✔
77
            return new TextResultsFormatter();
1✔
78
        }
79
        if ("xml".equalsIgnoreCase(formatterName)) {
1✔
80
            return new XMLResultsFormatter();
1✔
81
        }
82
        return new HTMLResultsFormatter();
1✔
83
    }
84

85
    /** The factory. */
86
    private InvocationContextFactory _factory;
87

88
    /**
89
     * Report cannot run test.
90
     *
91
     * @param writer
92
     *            the writer
93
     * @param errorMessage
94
     *            the error message
95
     */
96
    private void reportCannotRunTest(PrintWriter writer, final String errorMessage) {
97
        writer.print("<html><head><title>Cannot run test</title></head><body>" + errorMessage + "</body></html>");
1✔
98
    }
1✔
99

100
    /**
101
     * The Class ServletTestRunner.
102
     */
103
    class ServletTestRunner extends BaseTestRunner {
104

105
        /** The writer. */
106
        private PrintWriter _writer;
107

108
        /** The formatter. */
109
        private ResultsFormatter _formatter;
110

111
        /**
112
         * Instantiates a new servlet test runner.
113
         *
114
         * @param writer
115
         *            the writer
116
         * @param formatter
117
         *            the formatter
118
         */
119
        public ServletTestRunner(PrintWriter writer, ResultsFormatter formatter) {
1✔
120
            ServletTestCase.setInvocationContextFactory(_factory);
1✔
121
            _writer = writer;
1✔
122
            _formatter = formatter;
1✔
123
        }
1✔
124

125
        /**
126
         * Run test suite.
127
         *
128
         * @param testClassName
129
         *            the test class name
130
         */
131
        void runTestSuite(String testClassName) {
132
            Test suite = getTest(testClassName);
1✔
133

134
            if (suite != null) {
1✔
135
                TestResult testResult = new TestResult();
1✔
136
                testResult.addListener(this);
1✔
137
                long startTime = System.currentTimeMillis();
1✔
138
                suite.run(testResult);
1✔
139
                long endTime = System.currentTimeMillis();
1✔
140
                _formatter.displayResults(_writer, testClassName, elapsedTimeAsString(endTime - startTime), testResult);
1✔
141
            }
142
        }
1✔
143

144
        @Override
145
        public void addError(Test test, Throwable throwable) {
146
        }
1✔
147

148
        @Override
149
        public void addFailure(Test test, AssertionFailedError error) {
150
        }
1✔
151

152
        @Override
153
        public void endTest(Test test) {
154
        }
1✔
155

156
        @Override
157
        protected void runFailed(String s) {
158
            reportCannotRunTest(_writer, s);
1✔
159
        }
1✔
160

161
        @Override
162
        public void startTest(Test test) {
163
        }
1✔
164

165
        @Override
166
        public void testStarted(String s) {
167
        }
×
168

169
        @Override
170
        public void testEnded(String s) {
171
        }
×
172

173
        @Override
174
        public void testFailed(int i, Test test, Throwable throwable) {
175
        }
×
176

177
    }
178

179
    /**
180
     * The Class ResultsFormatter.
181
     */
182
    static abstract class ResultsFormatter {
1✔
183

184
        /** The Constant LF. */
185
        private static final char LF = 10;
186

187
        /** The Constant CR. */
188
        private static final char CR = 13;
189

190
        /**
191
         * Gets the content type.
192
         *
193
         * @return the content type
194
         */
195
        abstract String getContentType();
196

197
        /**
198
         * Display results.
199
         *
200
         * @param writer
201
         *            the writer
202
         * @param testClassName
203
         *            the test class name
204
         * @param elapsedTimeString
205
         *            the elapsed time string
206
         * @param testResult
207
         *            the test result
208
         */
209
        void displayResults(PrintWriter writer, String testClassName, String elapsedTimeString, TestResult testResult) {
210
            displayHeader(writer, testClassName, testResult, elapsedTimeString);
1✔
211
            displayResults(writer, testResult);
1✔
212
            displayFooter(writer);
1✔
213
        }
1✔
214

215
        /**
216
         * Display header.
217
         *
218
         * @param writer
219
         *            the writer
220
         * @param testClassName
221
         *            the test class name
222
         * @param testResult
223
         *            the test result
224
         * @param elapsedTimeString
225
         *            the elapsed time string
226
         */
227
        protected abstract void displayHeader(PrintWriter writer, String testClassName, TestResult testResult,
228
                String elapsedTimeString);
229

230
        /**
231
         * Display results.
232
         *
233
         * @param writer
234
         *            the writer
235
         * @param testResult
236
         *            the test result
237
         */
238
        protected abstract void displayResults(PrintWriter writer, TestResult testResult);
239

240
        /**
241
         * Display footer.
242
         *
243
         * @param writer
244
         *            the writer
245
         */
246
        protected abstract void displayFooter(PrintWriter writer);
247

248
        /**
249
         * Sgml escape.
250
         *
251
         * @param s
252
         *            the s
253
         *
254
         * @return the string
255
         */
256
        protected String sgmlEscape(String s) {
257
            if (s == null) {
1!
258
                return "NULL";
×
259
            }
260
            StringBuilder result = new StringBuilder(s.length());
1✔
261
            char[] chars = s.toCharArray();
1✔
262
            for (int i = 0; i < chars.length; i++) {
1✔
263
                switch (chars[i]) {
1!
264
                    case '&':
265
                        result.append("&amp;");
×
266
                        break;
×
267
                    case '<':
268
                        result.append("&lt;");
1✔
269
                        break;
1✔
270
                    case '>':
271
                        result.append("&gt;");
1✔
272
                        break;
1✔
273
                    case LF:
274
                        if (i > 0 && chars[i - 1] == CR) {
1!
275
                            result.append(chars[i]);
×
276
                            break;
×
277
                        }
278
                    case CR:
279
                        result.append(getLineBreak());
1✔
280
                    default:
281
                        result.append(chars[i]);
1✔
282
                }
283
            }
284
            return result.toString();
1✔
285
        }
286

287
        /**
288
         * Gets the line break.
289
         *
290
         * @return the line break
291
         */
292
        protected String getLineBreak() {
293
            return "<br>";
1✔
294
        }
295
    }
296

297
    /**
298
     * The Class DisplayedResultsFormatter.
299
     */
300
    abstract static class DisplayedResultsFormatter extends ResultsFormatter {
1✔
301

302
        @Override
303
        protected void displayHeader(PrintWriter writer, String testClassName, TestResult testResult,
304
                String elapsedTimeString) {
305
            displayHeader(writer, testClassName, getFormatted(testResult.runCount(), "test"), elapsedTimeString,
1✔
306
                    testResult.wasSuccessful() ? "OK" : "Problems Occurred");
1✔
307
        }
1✔
308

309
        @Override
310
        protected void displayResults(PrintWriter writer, TestResult testResult) {
311
            if (!testResult.wasSuccessful()) {
1✔
312
                displayProblems(writer, "failure", testResult.failureCount(), testResult.failures());
1✔
313
                displayProblems(writer, "error", testResult.errorCount(), testResult.errors());
1✔
314
            }
315
        }
1✔
316

317
        /**
318
         * Display header.
319
         *
320
         * @param writer
321
         *            the writer
322
         * @param testClassName
323
         *            the test class name
324
         * @param testCountText
325
         *            the test count text
326
         * @param elapsedTimeString
327
         *            the elapsed time string
328
         * @param resultString
329
         *            the result string
330
         */
331
        protected abstract void displayHeader(PrintWriter writer, String testClassName, String testCountText,
332
                String elapsedTimeString, String resultString);
333

334
        /**
335
         * Display problem title.
336
         *
337
         * @param writer
338
         *            the writer
339
         * @param title
340
         *            the title
341
         */
342
        protected abstract void displayProblemTitle(PrintWriter writer, String title);
343

344
        /**
345
         * Display problem detail header.
346
         *
347
         * @param writer
348
         *            the writer
349
         * @param i
350
         *            the i
351
         * @param testName
352
         *            the test name
353
         */
354
        protected abstract void displayProblemDetailHeader(PrintWriter writer, int i, String testName);
355

356
        /**
357
         * Display problem detail footer.
358
         *
359
         * @param writer
360
         *            the writer
361
         */
362
        protected abstract void displayProblemDetailFooter(PrintWriter writer);
363

364
        /**
365
         * Display problem detail.
366
         *
367
         * @param writer
368
         *            the writer
369
         * @param message
370
         *            the message
371
         */
372
        protected abstract void displayProblemDetail(PrintWriter writer, String message);
373

374
        /**
375
         * Display problems.
376
         *
377
         * @param writer
378
         *            the writer
379
         * @param kind
380
         *            the kind
381
         * @param count
382
         *            the count
383
         * @param enumeration
384
         *            the enumeration
385
         */
386
        private void displayProblems(PrintWriter writer, String kind, int count, Enumeration enumeration) {
387
            if (count != 0) {
1✔
388
                displayProblemTitle(writer, getFormatted(count, kind));
1✔
389
                Enumeration e = enumeration;
1✔
390
                for (int i = 1; e.hasMoreElements(); i++) {
1✔
391
                    TestFailure failure = (TestFailure) e.nextElement();
1✔
392
                    displayProblemDetailHeader(writer, i, failure.failedTest().toString());
1✔
393
                    if (failure.thrownException() instanceof AssertionFailedError) {
1✔
394
                        displayProblemDetail(writer, failure.thrownException().getMessage());
1✔
395
                    } else {
396
                        displayProblemDetail(writer, BaseTestRunner.getFilteredTrace(failure.thrownException()));
1✔
397
                    }
398
                    displayProblemDetailFooter(writer);
1✔
399
                }
400
            }
401
        }
1✔
402

403
        /**
404
         * Gets the formatted.
405
         *
406
         * @param count
407
         *            the count
408
         * @param name
409
         *            the name
410
         *
411
         * @return the formatted
412
         */
413
        private String getFormatted(int count, String name) {
414
            return count + " " + name + (count == 1 ? "" : "s");
1✔
415
        }
416

417
    }
418

419
    /**
420
     * The Class TextResultsFormatter.
421
     */
422
    static class TextResultsFormatter extends DisplayedResultsFormatter {
1✔
423

424
        @Override
425
        String getContentType() {
426
            return "text/plain";
1✔
427
        }
428

429
        @Override
430
        protected void displayHeader(PrintWriter writer, String testClassName, String testCountText,
431
                String elapsedTimeString, String resultString) {
432
            writer.println(testClassName + " (" + testCountText + "): " + resultString);
1✔
433
        }
1✔
434

435
        @Override
436
        protected void displayFooter(PrintWriter writer) {
437
        }
1✔
438

439
        @Override
440
        protected void displayProblemTitle(PrintWriter writer, String title) {
441
            writer.println();
1✔
442
            writer.println(title + ':');
1✔
443
        }
1✔
444

445
        @Override
446
        protected void displayProblemDetailHeader(PrintWriter writer, int i, String testName) {
447
            writer.println(i + ". " + testName + ":");
1✔
448
        }
1✔
449

450
        @Override
451
        protected void displayProblemDetailFooter(PrintWriter writer) {
452
            writer.println();
1✔
453
        }
1✔
454

455
        @Override
456
        protected void displayProblemDetail(PrintWriter writer, String message) {
457
            writer.println(message);
1✔
458
        }
1✔
459
    }
460

461
    /**
462
     * The Class HTMLResultsFormatter.
463
     */
464
    static class HTMLResultsFormatter extends DisplayedResultsFormatter {
1✔
465

466
        @Override
467
        String getContentType() {
468
            return "text/html";
1✔
469
        }
470

471
        @Override
472
        protected void displayHeader(PrintWriter writer, String testClassName, String testCountText,
473
                String elapsedTimeString, String resultString) {
474
            writer.println("<html><head><title>Test Suite: " + testClassName + "</title>");
1✔
475
            writer.println("<style type='text/css'>");
1✔
476
            writer.println("<!--");
1✔
477
            writer.println("  td.detail { font-size:smaller; vertical-align: top }");
1✔
478
            writer.println("  -->");
1✔
479
            writer.println("</style></head><body>");
1✔
480
            writer.println("<table id='results' border='1'><tr>");
1✔
481
            writer.println("<td>" + testCountText + "</td>");
1✔
482
            writer.println("<td>Time: " + elapsedTimeString + "</td>");
1✔
483
            writer.println("<td>" + resultString + "</td></tr>");
1✔
484
        }
1✔
485

486
        @Override
487
        protected void displayFooter(PrintWriter writer) {
488
            writer.println("</table>");
1✔
489
            writer.println("</body></html>");
1✔
490
        }
1✔
491

492
        @Override
493
        protected void displayProblemTitle(PrintWriter writer, String title) {
494
            writer.println("<tr><td colspan=3>" + title + "</td></tr>");
1✔
495
        }
1✔
496

497
        @Override
498
        protected void displayProblemDetailHeader(PrintWriter writer, int i, String testName) {
499
            writer.println("<tr><td class='detail' align='right'>" + i + "</td>");
1✔
500
            writer.println("<td class='detail'>" + testName + "</td><td class='detail'>");
1✔
501
        }
1✔
502

503
        @Override
504
        protected void displayProblemDetailFooter(PrintWriter writer) {
505
            writer.println("</td></tr>");
1✔
506
        }
1✔
507

508
        @Override
509
        protected void displayProblemDetail(PrintWriter writer, String message) {
510
            writer.println(sgmlEscape(message));
1✔
511
        }
1✔
512

513
    }
514

515
    /**
516
     * The Class XMLResultsFormatter.
517
     */
518
    static class XMLResultsFormatter extends ResultsFormatter {
1✔
519

520
        @Override
521
        String getContentType() {
522
            return "text/xml;charset=UTF-8";
1✔
523
        }
524

525
        @Override
526
        protected void displayHeader(PrintWriter writer, String testClassName, TestResult testResult,
527
                String elapsedTimeString) {
528
            writer.println("<?xml version='1.0' encoding='UTF-8' ?>\n" + "<testsuite name=" + asAttribute(testClassName)
1✔
529
                    + " tests=" + asAttribute(testResult.runCount()) + " failures="
1✔
530
                    + asAttribute(testResult.failureCount()) + " errors=" + asAttribute(testResult.errorCount())
1✔
531
                    + " time=" + asAttribute(elapsedTimeString) + ">");
1✔
532
        }
1✔
533

534
        /**
535
         * As attribute.
536
         *
537
         * @param value
538
         *            the value
539
         *
540
         * @return the string
541
         */
542
        private String asAttribute(int value) {
543
            return '"' + Integer.toString(value) + '"';
1✔
544
        }
545

546
        /**
547
         * As attribute.
548
         *
549
         * @param value
550
         *            the value
551
         *
552
         * @return the string
553
         */
554
        private String asAttribute(String value) {
555
            return '"' + sgmlEscape(value) + '"';
1✔
556
        }
557

558
        @Override
559
        protected void displayFooter(PrintWriter writer) {
560
            writer.println("</testsuite>");
1✔
561
        }
1✔
562

563
        @Override
564
        protected void displayResults(PrintWriter writer, TestResult testResult) {
565
            displayResults(writer, "failure", testResult.failures());
1✔
566
            displayResults(writer, "error", testResult.errors());
1✔
567
        }
1✔
568

569
        /**
570
         * Display results.
571
         *
572
         * @param writer
573
         *            the writer
574
         * @param failureNodeName
575
         *            the failure node name
576
         * @param resultsEnumeration
577
         *            the results enumeration
578
         */
579
        private void displayResults(PrintWriter writer, String failureNodeName, Enumeration resultsEnumeration) {
580
            for (Enumeration e = resultsEnumeration; e.hasMoreElements();) {
1✔
581
                TestFailure failure = (TestFailure) e.nextElement();
1✔
582
                writer.println("  <testcase name=" + asAttribute(failure.failedTest().toString()) + ">");
1✔
583
                writer.print("    <" + failureNodeName + " type="
1✔
584
                        + asAttribute(failure.thrownException().getClass().getName()) + " message="
1✔
585
                        + asAttribute(failure.exceptionMessage()));
1✔
586
                if (!displayException()) {
1!
587
                    writer.println("/>");
×
588
                } else {
589
                    writer.println(">");
1✔
590
                    writer.print(sgmlEscape(BaseTestRunner.getFilteredTrace(failure.thrownException())));
1✔
591
                    writer.println("    </" + failureNodeName + ">");
1✔
592
                }
593
                writer.println("  </testcase>");
1✔
594
            }
1✔
595
        }
1✔
596

597
        /**
598
         * Display exception.
599
         *
600
         * @return true, if successful
601
         */
602
        private boolean displayException() {
603
            return true;
1✔
604
        }
605

606
        @Override
607
        protected String getLineBreak() {
608
            return "";
1✔
609
        }
610
    }
611

612
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc