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

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

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
 * MIT License
3
 *
4
 * Copyright 2011-2025 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.servletunit;
21

22
import java.io.IOException;
23
import java.io.PrintWriter;
24
import java.util.Enumeration;
25

26
import javax.servlet.ServletException;
27
import javax.servlet.http.HttpServlet;
28
import javax.servlet.http.HttpServletRequest;
29
import javax.servlet.http.HttpServletResponse;
30

31
import junit.framework.AssertionFailedError;
32
import junit.framework.Test;
33
import junit.framework.TestFailure;
34
import junit.framework.TestResult;
35
import junit.runner.BaseTestRunner;
36

37
/**
38
 * A servlet which can run unit tests inside a servlet context. It may be extended to provide InvocationContext-access
39
 * to such tests if a container-specific implementation of InvocationContextFactory is provided. Combined with
40
 * ServletTestCase, this would permit in-container tests of servlets in a fashion similar to that supported by
41
 * ServletUnit.
42
 **/
43
public class JUnitServlet extends HttpServlet {
44

45
    /** The Constant serialVersionUID. */
46
    private static final long serialVersionUID = 1L;
47

48
    /**
49
     * Instantiates a new j unit servlet.
50
     */
51
    public JUnitServlet() {
×
52
    }
×
53

54
    /**
55
     * Instantiates a new j unit servlet.
56
     *
57
     * @param factory
58
     *            the factory
59
     */
60
    protected JUnitServlet(InvocationContextFactory factory) {
1✔
61
        _factory = factory;
1✔
62
    }
1✔
63

64
    @Override
65
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
66
            throws ServletException, IOException {
67
        ResultsFormatter formatter = getResultsFormatter(request.getParameter("format"));
1✔
68
        response.setContentType(formatter.getContentType());
1✔
69
        final String testName = request.getParameter("test");
1✔
70
        if (testName == null || testName.isEmpty()) {
1!
71
            reportCannotRunTest(response.getWriter(), "No test class specified");
1✔
72
        } else {
73
            ServletTestRunner runner = new ServletTestRunner(response.getWriter(), formatter);
1✔
74
            runner.runTestSuite(testName);
1✔
75
        }
76
        response.getWriter().close();
1✔
77
    }
1✔
78

79
    /**
80
     * Gets the results formatter.
81
     *
82
     * @param formatterName
83
     *            the formatter name
84
     *
85
     * @return the results formatter
86
     */
87
    private ResultsFormatter getResultsFormatter(String formatterName) {
88
        if ("text".equalsIgnoreCase(formatterName)) {
1✔
89
            return new TextResultsFormatter();
1✔
90
        }
91
        if ("xml".equalsIgnoreCase(formatterName)) {
1✔
92
            return new XMLResultsFormatter();
1✔
93
        }
94
        return new HTMLResultsFormatter();
1✔
95
    }
96

97
    /** The factory. */
98
    private InvocationContextFactory _factory;
99

100
    /**
101
     * Report cannot run test.
102
     *
103
     * @param writer
104
     *            the writer
105
     * @param errorMessage
106
     *            the error message
107
     */
108
    private void reportCannotRunTest(PrintWriter writer, final String errorMessage) {
109
        writer.print("<html><head><title>Cannot run test</title></head><body>" + errorMessage + "</body></html>");
1✔
110
    }
1✔
111

112
    /**
113
     * The Class ServletTestRunner.
114
     */
115
    class ServletTestRunner extends BaseTestRunner {
116

117
        /** The writer. */
118
        private PrintWriter _writer;
119

120
        /** The formatter. */
121
        private ResultsFormatter _formatter;
122

123
        /**
124
         * Instantiates a new servlet test runner.
125
         *
126
         * @param writer
127
         *            the writer
128
         * @param formatter
129
         *            the formatter
130
         */
131
        public ServletTestRunner(PrintWriter writer, ResultsFormatter formatter) {
1✔
132
            ServletTestCase.setInvocationContextFactory(_factory);
1✔
133
            _writer = writer;
1✔
134
            _formatter = formatter;
1✔
135
        }
1✔
136

137
        /**
138
         * Run test suite.
139
         *
140
         * @param testClassName
141
         *            the test class name
142
         */
143
        void runTestSuite(String testClassName) {
144
            Test suite = getTest(testClassName);
1✔
145

146
            if (suite != null) {
1✔
147
                TestResult testResult = new TestResult();
1✔
148
                testResult.addListener(this);
1✔
149
                long startTime = System.currentTimeMillis();
1✔
150
                suite.run(testResult);
1✔
151
                long endTime = System.currentTimeMillis();
1✔
152
                _formatter.displayResults(_writer, testClassName, elapsedTimeAsString(endTime - startTime), testResult);
1✔
153
            }
154
        }
1✔
155

156
        @Override
157
        public void addError(Test test, Throwable throwable) {
158
        }
1✔
159

160
        @Override
161
        public void addFailure(Test test, AssertionFailedError error) {
162
        }
1✔
163

164
        @Override
165
        public void endTest(Test test) {
166
        }
1✔
167

168
        @Override
169
        protected void runFailed(String s) {
170
            reportCannotRunTest(_writer, s);
1✔
171
        }
1✔
172

173
        @Override
174
        public void startTest(Test test) {
175
        }
1✔
176

177
        @Override
178
        public void testStarted(String s) {
179
        }
×
180

181
        @Override
182
        public void testEnded(String s) {
183
        }
×
184

185
        @Override
186
        public void testFailed(int i, Test test, Throwable throwable) {
187
        }
×
188

189
    }
190

191
    /**
192
     * The Class ResultsFormatter.
193
     */
194
    static abstract class ResultsFormatter {
1✔
195

196
        /** The Constant LF. */
197
        private static final char LF = 10;
198

199
        /** The Constant CR. */
200
        private static final char CR = 13;
201

202
        /**
203
         * Gets the content type.
204
         *
205
         * @return the content type
206
         */
207
        abstract String getContentType();
208

209
        /**
210
         * Display results.
211
         *
212
         * @param writer
213
         *            the writer
214
         * @param testClassName
215
         *            the test class name
216
         * @param elapsedTimeString
217
         *            the elapsed time string
218
         * @param testResult
219
         *            the test result
220
         */
221
        void displayResults(PrintWriter writer, String testClassName, String elapsedTimeString, TestResult testResult) {
222
            displayHeader(writer, testClassName, testResult, elapsedTimeString);
1✔
223
            displayResults(writer, testResult);
1✔
224
            displayFooter(writer);
1✔
225
        }
1✔
226

227
        /**
228
         * Display header.
229
         *
230
         * @param writer
231
         *            the writer
232
         * @param testClassName
233
         *            the test class name
234
         * @param testResult
235
         *            the test result
236
         * @param elapsedTimeString
237
         *            the elapsed time string
238
         */
239
        protected abstract void displayHeader(PrintWriter writer, String testClassName, TestResult testResult,
240
                String elapsedTimeString);
241

242
        /**
243
         * Display results.
244
         *
245
         * @param writer
246
         *            the writer
247
         * @param testResult
248
         *            the test result
249
         */
250
        protected abstract void displayResults(PrintWriter writer, TestResult testResult);
251

252
        /**
253
         * Display footer.
254
         *
255
         * @param writer
256
         *            the writer
257
         */
258
        protected abstract void displayFooter(PrintWriter writer);
259

260
        /**
261
         * Sgml escape.
262
         *
263
         * @param s
264
         *            the s
265
         *
266
         * @return the string
267
         */
268
        protected String sgmlEscape(String s) {
269
            if (s == null) {
1!
270
                return "NULL";
×
271
            }
272
            StringBuilder result = new StringBuilder(s.length());
1✔
273
            char[] chars = s.toCharArray();
1✔
274
            for (int i = 0; i < chars.length; i++) {
1✔
275
                switch (chars[i]) {
1!
276
                    case '&':
277
                        result.append("&amp;");
×
278
                        break;
×
279
                    case '<':
280
                        result.append("&lt;");
1✔
281
                        break;
1✔
282
                    case '>':
283
                        result.append("&gt;");
1✔
284
                        break;
1✔
285
                    case LF:
286
                        if (i > 0 && chars[i - 1] == CR) {
1!
287
                            result.append(chars[i]);
×
288
                            break;
×
289
                        }
290
                    case CR:
291
                        result.append(getLineBreak());
1✔
292
                    default:
293
                        result.append(chars[i]);
1✔
294
                }
295
            }
296
            return result.toString();
1✔
297
        }
298

299
        /**
300
         * Gets the line break.
301
         *
302
         * @return the line break
303
         */
304
        protected String getLineBreak() {
305
            return "<br>";
1✔
306
        }
307
    }
308

309
    /**
310
     * The Class DisplayedResultsFormatter.
311
     */
312
    abstract static class DisplayedResultsFormatter extends ResultsFormatter {
1✔
313

314
        @Override
315
        protected void displayHeader(PrintWriter writer, String testClassName, TestResult testResult,
316
                String elapsedTimeString) {
317
            displayHeader(writer, testClassName, getFormatted(testResult.runCount(), "test"), elapsedTimeString,
1✔
318
                    testResult.wasSuccessful() ? "OK" : "Problems Occurred");
1✔
319
        }
1✔
320

321
        @Override
322
        protected void displayResults(PrintWriter writer, TestResult testResult) {
323
            if (!testResult.wasSuccessful()) {
1✔
324
                displayProblems(writer, "failure", testResult.failureCount(), testResult.failures());
1✔
325
                displayProblems(writer, "error", testResult.errorCount(), testResult.errors());
1✔
326
            }
327
        }
1✔
328

329
        /**
330
         * Display header.
331
         *
332
         * @param writer
333
         *            the writer
334
         * @param testClassName
335
         *            the test class name
336
         * @param testCountText
337
         *            the test count text
338
         * @param elapsedTimeString
339
         *            the elapsed time string
340
         * @param resultString
341
         *            the result string
342
         */
343
        protected abstract void displayHeader(PrintWriter writer, String testClassName, String testCountText,
344
                String elapsedTimeString, String resultString);
345

346
        /**
347
         * Display problem title.
348
         *
349
         * @param writer
350
         *            the writer
351
         * @param title
352
         *            the title
353
         */
354
        protected abstract void displayProblemTitle(PrintWriter writer, String title);
355

356
        /**
357
         * Display problem detail header.
358
         *
359
         * @param writer
360
         *            the writer
361
         * @param i
362
         *            the i
363
         * @param testName
364
         *            the test name
365
         */
366
        protected abstract void displayProblemDetailHeader(PrintWriter writer, int i, String testName);
367

368
        /**
369
         * Display problem detail footer.
370
         *
371
         * @param writer
372
         *            the writer
373
         */
374
        protected abstract void displayProblemDetailFooter(PrintWriter writer);
375

376
        /**
377
         * Display problem detail.
378
         *
379
         * @param writer
380
         *            the writer
381
         * @param message
382
         *            the message
383
         */
384
        protected abstract void displayProblemDetail(PrintWriter writer, String message);
385

386
        /**
387
         * Display problems.
388
         *
389
         * @param writer
390
         *            the writer
391
         * @param kind
392
         *            the kind
393
         * @param count
394
         *            the count
395
         * @param enumeration
396
         *            the enumeration
397
         */
398
        private void displayProblems(PrintWriter writer, String kind, int count, Enumeration enumeration) {
399
            if (count != 0) {
1✔
400
                displayProblemTitle(writer, getFormatted(count, kind));
1✔
401
                Enumeration e = enumeration;
1✔
402
                for (int i = 1; e.hasMoreElements(); i++) {
1✔
403
                    TestFailure failure = (TestFailure) e.nextElement();
1✔
404
                    displayProblemDetailHeader(writer, i, failure.failedTest().toString());
1✔
405
                    if (failure.thrownException() instanceof AssertionFailedError) {
1✔
406
                        displayProblemDetail(writer, failure.thrownException().getMessage());
1✔
407
                    } else {
408
                        displayProblemDetail(writer, BaseTestRunner.getFilteredTrace(failure.thrownException()));
1✔
409
                    }
410
                    displayProblemDetailFooter(writer);
1✔
411
                }
412
            }
413
        }
1✔
414

415
        /**
416
         * Gets the formatted.
417
         *
418
         * @param count
419
         *            the count
420
         * @param name
421
         *            the name
422
         *
423
         * @return the formatted
424
         */
425
        private String getFormatted(int count, String name) {
426
            return count + " " + name + (count == 1 ? "" : "s");
1✔
427
        }
428

429
    }
430

431
    /**
432
     * The Class TextResultsFormatter.
433
     */
434
    static class TextResultsFormatter extends DisplayedResultsFormatter {
1✔
435

436
        @Override
437
        String getContentType() {
438
            return "text/plain";
1✔
439
        }
440

441
        @Override
442
        protected void displayHeader(PrintWriter writer, String testClassName, String testCountText,
443
                String elapsedTimeString, String resultString) {
444
            writer.println(testClassName + " (" + testCountText + "): " + resultString);
1✔
445
        }
1✔
446

447
        @Override
448
        protected void displayFooter(PrintWriter writer) {
449
        }
1✔
450

451
        @Override
452
        protected void displayProblemTitle(PrintWriter writer, String title) {
453
            writer.println();
1✔
454
            writer.println(title + ':');
1✔
455
        }
1✔
456

457
        @Override
458
        protected void displayProblemDetailHeader(PrintWriter writer, int i, String testName) {
459
            writer.println(i + ". " + testName + ":");
1✔
460
        }
1✔
461

462
        @Override
463
        protected void displayProblemDetailFooter(PrintWriter writer) {
464
            writer.println();
1✔
465
        }
1✔
466

467
        @Override
468
        protected void displayProblemDetail(PrintWriter writer, String message) {
469
            writer.println(message);
1✔
470
        }
1✔
471
    }
472

473
    /**
474
     * The Class HTMLResultsFormatter.
475
     */
476
    static class HTMLResultsFormatter extends DisplayedResultsFormatter {
1✔
477

478
        @Override
479
        String getContentType() {
480
            return "text/html";
1✔
481
        }
482

483
        @Override
484
        protected void displayHeader(PrintWriter writer, String testClassName, String testCountText,
485
                String elapsedTimeString, String resultString) {
486
            writer.println("<html><head><title>Test Suite: " + testClassName + "</title>");
1✔
487
            writer.println("<style type='text/css'>");
1✔
488
            writer.println("<!--");
1✔
489
            writer.println("  td.detail { font-size:smaller; vertical-align: top }");
1✔
490
            writer.println("  -->");
1✔
491
            writer.println("</style></head><body>");
1✔
492
            writer.println("<table id='results' border='1'><tr>");
1✔
493
            writer.println("<td>" + testCountText + "</td>");
1✔
494
            writer.println("<td>Time: " + elapsedTimeString + "</td>");
1✔
495
            writer.println("<td>" + resultString + "</td></tr>");
1✔
496
        }
1✔
497

498
        @Override
499
        protected void displayFooter(PrintWriter writer) {
500
            writer.println("</table>");
1✔
501
            writer.println("</body></html>");
1✔
502
        }
1✔
503

504
        @Override
505
        protected void displayProblemTitle(PrintWriter writer, String title) {
506
            writer.println("<tr><td colspan=3>" + title + "</td></tr>");
1✔
507
        }
1✔
508

509
        @Override
510
        protected void displayProblemDetailHeader(PrintWriter writer, int i, String testName) {
511
            writer.println("<tr><td class='detail' align='right'>" + i + "</td>");
1✔
512
            writer.println("<td class='detail'>" + testName + "</td><td class='detail'>");
1✔
513
        }
1✔
514

515
        @Override
516
        protected void displayProblemDetailFooter(PrintWriter writer) {
517
            writer.println("</td></tr>");
1✔
518
        }
1✔
519

520
        @Override
521
        protected void displayProblemDetail(PrintWriter writer, String message) {
522
            writer.println(sgmlEscape(message));
1✔
523
        }
1✔
524

525
    }
526

527
    /**
528
     * The Class XMLResultsFormatter.
529
     */
530
    static class XMLResultsFormatter extends ResultsFormatter {
1✔
531

532
        @Override
533
        String getContentType() {
534
            return "text/xml;charset=UTF-8";
1✔
535
        }
536

537
        @Override
538
        protected void displayHeader(PrintWriter writer, String testClassName, TestResult testResult,
539
                String elapsedTimeString) {
540
            writer.println("<?xml version='1.0' encoding='UTF-8' ?>\n" + "<testsuite name=" + asAttribute(testClassName)
1✔
541
                    + " tests=" + asAttribute(testResult.runCount()) + " failures="
1✔
542
                    + asAttribute(testResult.failureCount()) + " errors=" + asAttribute(testResult.errorCount())
1✔
543
                    + " time=" + asAttribute(elapsedTimeString) + ">");
1✔
544
        }
1✔
545

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

558
        /**
559
         * As attribute.
560
         *
561
         * @param value
562
         *            the value
563
         *
564
         * @return the string
565
         */
566
        private String asAttribute(String value) {
567
            return '"' + sgmlEscape(value) + '"';
1✔
568
        }
569

570
        @Override
571
        protected void displayFooter(PrintWriter writer) {
572
            writer.println("</testsuite>");
1✔
573
        }
1✔
574

575
        @Override
576
        protected void displayResults(PrintWriter writer, TestResult testResult) {
577
            displayResults(writer, "failure", testResult.failures());
1✔
578
            displayResults(writer, "error", testResult.errors());
1✔
579
        }
1✔
580

581
        /**
582
         * Display results.
583
         *
584
         * @param writer
585
         *            the writer
586
         * @param failureNodeName
587
         *            the failure node name
588
         * @param resultsEnumeration
589
         *            the results enumeration
590
         */
591
        private void displayResults(PrintWriter writer, String failureNodeName, Enumeration resultsEnumeration) {
592
            for (Enumeration e = resultsEnumeration; e.hasMoreElements();) {
1✔
593
                TestFailure failure = (TestFailure) e.nextElement();
1✔
594
                writer.println("  <testcase name=" + asAttribute(failure.failedTest().toString()) + ">");
1✔
595
                writer.print("    <" + failureNodeName + " type="
1✔
596
                        + asAttribute(failure.thrownException().getClass().getName()) + " message="
1✔
597
                        + asAttribute(failure.exceptionMessage()));
1✔
598
                if (!displayException()) {
1!
599
                    writer.println("/>");
×
600
                } else {
601
                    writer.println(">");
1✔
602
                    writer.print(sgmlEscape(BaseTestRunner.getFilteredTrace(failure.thrownException())));
1✔
603
                    writer.println("    </" + failureNodeName + ">");
1✔
604
                }
605
                writer.println("  </testcase>");
1✔
606
            }
1✔
607
        }
1✔
608

609
        /**
610
         * Display exception.
611
         *
612
         * @return true, if successful
613
         */
614
        private boolean displayException() {
615
            return true;
1✔
616
        }
617

618
        @Override
619
        protected String getLineBreak() {
620
            return "";
1✔
621
        }
622
    }
623

624
}
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