码迷,mamicode.com
首页 > 编程语言 > 详细

Python2 HTMLTestRunner自动化测试报告美化

时间:2019-01-24 16:34:38      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:oct   future   ||   HERE   ica   output   变换   需要   param   

python2 的测试报告美化,需要的同学直接用

  1 #coding=utf-8
  2 """
  3 A TestRunner for use with the Python unit testing framework. It
  4 generates a HTML report to show the result at a glance.
  5 
  6 The simplest way to use this is to invoke its main method. E.g.
  7 
  8     import unittest
  9     import HTMLTestRunner
 10 
 11     ... define your tests ...
 12 
 13     if __name__ == ‘__main__‘:
 14         HTMLTestRunner.main()
 15 
 16 
 17 For more customization options, instantiates a HTMLTestRunner object.
 18 HTMLTestRunner is a counterpart to unittest‘s TextTestRunner. E.g.
 19 
 20     # output to a file
 21     fp = file(‘my_report.html‘, ‘wb‘)
 22     runner = HTMLTestRunner.HTMLTestRunner(
 23                 stream=fp,
 24                 title=‘My unit test‘,
 25                 description=‘This demonstrates the report output by HTMLTestRunner.‘
 26                 )
 27 
 28     # Use an external stylesheet.
 29     # See the Template_mixin class for more customizable options
 30     runner.STYLESHEET_TMPL = ‘<link rel="stylesheet" href="my_stylesheet.css" type="text/css">‘
 31 
 32     # run the test
 33     runner.run(my_test_suite)
 34 
 35 
 36 ------------------------------------------------------------------------
 37 Copyright (c) 2004-2007, Wai Yip Tung
 38 All rights reserved.
 39 
 40 Redistribution and use in source and binary forms, with or without
 41 modification, are permitted provided that the following conditions are
 42 met:
 43 
 44 * Redistributions of source code must retain the above copyright notice,
 45   this list of conditions and the following disclaimer.
 46 * Redistributions in binary form must reproduce the above copyright
 47   notice, this list of conditions and the following disclaimer in the
 48   documentation and/or other materials provided with the distribution.
 49 * Neither the name Wai Yip Tung nor the names of its contributors may be
 50   used to endorse or promote products derived from this software without
 51   specific prior written permission.
 52 
 53 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 54 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 55 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 56 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 57 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 58 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 59 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 60 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 61 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 62 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 63 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 64 """
 65 
 66 # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
 67 
 68 __author__ = "Wai Yip Tung,  Findyou"
 69 __version__ = "0.8.2.1"
 70 
 71 
 72 """
 73 Change History
 74 
 75 Version 0.8.2.1 -Findyou
 76 * 支持中文,汉化
 77 * 调整样式,美化(需要连入网络,使用的百度的Bootstrap.js)
 78 * 增加 通过分类显示、测试人员、通过率的展示
 79 * 优化“详细”与“收起”状态的变换
 80 * 增加返回顶部的锚点
 81 
 82 Version 0.8.2
 83 * Show output inline instead of popup window (Viorel Lupu).
 84 
 85 Version in 0.8.1
 86 * Validated XHTML (Wolfgang Borgert).
 87 * Added description of test classes and test cases.
 88 
 89 Version in 0.8.0
 90 * Define Template_mixin class for customization.
 91 * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
 92 
 93 Version in 0.7.1
 94 * Back port to Python 2.3 (Frank Horowitz).
 95 * Fix missing scroll bars in detail log (Podi).
 96 """
 97 
 98 # TODO: color stderr
 99 # TODO: simplify javascript using ,ore than 1 class in the class attribute?
100 
101 import datetime
102 import StringIO
103 import sys
104 import time
105 import unittest
106 from xml.sax import saxutils
107 import sys
108 reload(sys)
109 sys.setdefaultencoding(utf-8)
110 
111 # ------------------------------------------------------------------------
112 # The redirectors below are used to capture output during testing. Output
113 # sent to sys.stdout and sys.stderr are automatically captured. However
114 # in some cases sys.stdout is already cached before HTMLTestRunner is
115 # invoked (e.g. calling logging.basicConfig). In order to capture those
116 # output, use the redirectors for the cached stream.
117 #
118 # e.g.
119 #   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
120 #   >>>
121 
122 class OutputRedirector(object):
123     """ Wrapper to redirect stdout or stderr """
124     def __init__(self, fp):
125         self.fp = fp
126 
127     def write(self, s):
128         self.fp.write(s)
129 
130     def writelines(self, lines):
131         self.fp.writelines(lines)
132 
133     def flush(self):
134         self.fp.flush()
135 
136 stdout_redirector = OutputRedirector(sys.stdout)
137 stderr_redirector = OutputRedirector(sys.stderr)
138 
139 # ----------------------------------------------------------------------
140 # Template
141 
142 class Template_mixin(object):
143     """
144     Define a HTML template for report customerization and generation.
145 
146     Overall structure of an HTML report
147 
148     HTML
149     +------------------------+
150     |<html>                  |
151     |  <head>                |
152     |                        |
153     |   STYLESHEET           |
154     |   +----------------+   |
155     |   |                |   |
156     |   +----------------+   |
157     |                        |
158     |  </head>               |
159     |                        |
160     |  <body>                |
161     |                        |
162     |   HEADING              |
163     |   +----------------+   |
164     |   |                |   |
165     |   +----------------+   |
166     |                        |
167     |   REPORT               |
168     |   +----------------+   |
169     |   |                |   |
170     |   +----------------+   |
171     |                        |
172     |   ENDING               |
173     |   +----------------+   |
174     |   |                |   |
175     |   +----------------+   |
176     |                        |
177     |  </body>               |
178     |</html>                 |
179     +------------------------+
180     """
181 
182     STATUS = {
183     0: 通过,
184     1: 失败,
185     2: 错误,
186     }
187 
188     DEFAULT_TITLE = 单元测试报告
189     DEFAULT_DESCRIPTION = ‘‘
190     DEFAULT_TESTER=WangYingHao
191 
192     # ------------------------------------------------------------------------
193     # HTML Template
194 
195     HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
196 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
197 <html xmlns="http://www.w3.org/1999/xhtml">
198 <head>
199     <title>%(title)s</title>
200     <meta name="generator" content="%(generator)s"/>
201     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
202     <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
203     <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
204     <script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
205     %(stylesheet)s
206 </head>
207 <body >
208 <script language="javascript" type="text/javascript">
209 output_list = Array();
210 
211 /*level 调整增加只显示通过用例的分类 --Findyou
212 0:Summary //all hiddenRow
213 1:Failed  //pt hiddenRow, ft none
214 2:Pass    //pt none, ft hiddenRow
215 3:All     //pt none, ft none
216 */
217 function showCase(level) {
218     trs = document.getElementsByTagName("tr");
219     for (var i = 0; i < trs.length; i++) {
220         tr = trs[i];
221         id = tr.id;
222         if (id.substr(0,2) == ‘ft‘) {
223             if (level == 2 || level == 0 ) {
224                 tr.className = ‘hiddenRow‘;
225             }
226             else {
227                 tr.className = ‘‘;
228             }
229         }
230         if (id.substr(0,2) == ‘pt‘) {
231             if (level < 2) {
232                 tr.className = ‘hiddenRow‘;
233             }
234             else {
235                 tr.className = ‘‘;
236             }
237         }
238     }
239 
240     //加入【详细】切换文字变化 --Findyou
241     detail_class=document.getElementsByClassName(‘detail‘);
242     //console.log(detail_class.length)
243     if (level == 3) {
244         for (var i = 0; i < detail_class.length; i++){
245             detail_class[i].innerHTML="收起"
246         }
247     }
248     else{
249             for (var i = 0; i < detail_class.length; i++){
250             detail_class[i].innerHTML="详细"
251         }
252     }
253 }
254 
255 function showClassDetail(cid, count) {
256     var id_list = Array(count);
257     var toHide = 1;
258     for (var i = 0; i < count; i++) {
259         //ID修改 点 为 下划线 -Findyou
260         tid0 = ‘t‘ + cid.substr(1) + ‘_‘ + (i+1);
261         tid = ‘f‘ + tid0;
262         tr = document.getElementById(tid);
263         if (!tr) {
264             tid = ‘p‘ + tid0;
265             tr = document.getElementById(tid);
266         }
267         id_list[i] = tid;
268         if (tr.className) {
269             toHide = 0;
270         }
271     }
272     for (var i = 0; i < count; i++) {
273         tid = id_list[i];
274         //修改点击无法收起的BUG,加入【详细】切换文字变化 --Findyou
275         if (toHide) {
276             document.getElementById(tid).className = ‘hiddenRow‘;
277             document.getElementById(cid).innerText = "详细"
278         }
279         else {
280             document.getElementById(tid).className = ‘‘;
281             document.getElementById(cid).innerText = "收起"
282         }
283     }
284 }
285 
286 function html_escape(s) {
287     s = s.replace(/&/g,‘&amp;‘);
288     s = s.replace(/</g,‘&lt;‘);
289     s = s.replace(/>/g,‘&gt;‘);
290     return s;
291 }
292 </script>
293 %(heading)s
294 %(report)s
295 %(ending)s
296 
297 </body>
298 </html>
299 """
300     # variables: (title, generator, stylesheet, heading, report, ending)
301 
302 
303     # ------------------------------------------------------------------------
304     # Stylesheet
305     #
306     # alternatively use a <link> for external style sheet, e.g.
307     #   <link rel="stylesheet" href="$url" type="text/css">
308 
309     STYLESHEET_TMPL = """
310 <style type="text/css" media="screen">
311 body        { font-family: Microsoft YaHei,Tahoma,arial,helvetica,sans-serif;padding: 20px; font-size: 80%; }
312 table       { font-size: 100%; }
313 
314 /* -- heading ---------------------------------------------------------------------- */
315 .heading {
316     margin-top: 0ex;
317     margin-bottom: 1ex;
318 }
319 
320 .heading .description {
321     margin-top: 4ex;
322     margin-bottom: 6ex;
323 }
324 
325 /* -- report ------------------------------------------------------------------------ */
326 #total_row  { font-weight: bold; }
327 .passCase   { color: #5cb85c; }
328 .failCase   { color: #d9534f; font-weight: bold; }
329 .errorCase  { color: #f0ad4e; font-weight: bold; }
330 .hiddenRow  { display: none; }
331 .testcase   { margin-left: 2em; }
332 </style>
333 """
334 
335     # ------------------------------------------------------------------------
336     # Heading
337     #
338 
339     HEADING_TMPL = """<div class=‘heading‘>
340 <h1 style="font-family: Microsoft YaHei">%(title)s</h1>
341 %(parameters)s
342 <p class=‘description‘>%(description)s</p>
343 </div>
344 
345 """ # variables: (title, parameters, description)
346 
347     HEADING_ATTRIBUTE_TMPL = """<p class=‘attribute‘><strong>%(name)s : </strong> %(value)s</p>
348 """ # variables: (name, value)
349 
350 
351 
352     # ------------------------------------------------------------------------
353     # Report
354     #
355     # 汉化,加美化效果 --Findyou
356     REPORT_TMPL = """
357 <p id=‘show_detail_line‘>
358 <a class="btn btn-primary" href=‘javascript:showCase(0)‘>概要{ %(passrate)s }</a>
359 <a class="btn btn-danger" href=‘javascript:showCase(1)‘>失败{ %(fail)s }</a>
360 <a class="btn btn-success" href=‘javascript:showCase(2)‘>通过{ %(Pass)s }</a>
361 <a class="btn btn-info" href=‘javascript:showCase(3)‘>所有{ %(count)s }</a>
362 </p>
363 <table id=‘result_table‘ class="table table-condensed table-bordered table-hover">
364 <colgroup>
365 <col align=‘left‘ />
366 <col align=‘right‘ />
367 <col align=‘right‘ />
368 <col align=‘right‘ />
369 <col align=‘right‘ />
370 <col align=‘right‘ />
371 </colgroup>
372 <tr id=‘header_row‘ class="text-center success" style="font-weight: bold;font-size: 14px;">
373     <td>用例集/测试用例</td>
374     <td>总计</td>
375     <td>通过</td>
376     <td>失败</td>
377     <td>错误</td>
378     <td>详细</td>
379 </tr>
380 %(test_list)s
381 <tr id=‘total_row‘ class="text-center active">
382     <td>总计</td>
383     <td>%(count)s</td>
384     <td>%(Pass)s</td>
385     <td>%(fail)s</td>
386     <td>%(error)s</td>
387     <td>通过率:%(passrate)s</td>
388 </tr>
389 </table>
390 """ # variables: (test_list, count, Pass, fail, error ,passrate)
391 
392     REPORT_CLASS_TMPL = r"""
393 <tr class=‘%(style)s warning‘>
394     <td>%(desc)s</td>
395     <td class="text-center">%(count)s</td>
396     <td class="text-center">%(Pass)s</td>
397     <td class="text-center">%(fail)s</td>
398     <td class="text-center">%(error)s</td>
399     <td class="text-center"><a href="javascript:showClassDetail(‘%(cid)s‘,%(count)s)" class="detail" id=‘%(cid)s‘>详细</a></td>
400 </tr>
401 """ # variables: (style, desc, count, Pass, fail, error, cid)
402 
403     #失败 的样式,去掉原来JS效果,美化展示效果  -Findyou
404     REPORT_TEST_WITH_OUTPUT_TMPL = r"""
405 <tr id=‘%(tid)s‘ class=‘%(Class)s‘>
406     <td class=‘%(style)s‘><div class=‘testcase‘>%(desc)s</div></td>
407     <td colspan=‘5‘ align=‘center‘>
408     <!--默认收起错误信息 -Findyou
409     <button id=‘btn_%(tid)s‘ type="button"  class="btn btn-danger btn-xs collapsed" data-toggle="collapse" data-target=‘#div_%(tid)s‘>%(status)s</button>
410     <div id=‘div_%(tid)s‘ class="collapse">  -->
411 
412     <!-- 默认展开错误信息 -Findyou -->
413     <button id=‘btn_%(tid)s‘ type="button"  class="btn btn-danger btn-xs" data-toggle="collapse" data-target=‘#div_%(tid)s‘>%(status)s</button>
414     <div id=‘div_%(tid)s‘ class="collapse in">
415     <pre>
416     %(script)s
417     </pre>
418     </div>
419     </td>
420 </tr>
421 """ # variables: (tid, Class, style, desc, status)
422 
423     # 通过 的样式,加标签效果  -Findyou
424     REPORT_TEST_NO_OUTPUT_TMPL = r"""
425 <tr id=‘%(tid)s‘ class=‘%(Class)s‘>
426     <td class=‘%(style)s‘><div class=‘testcase‘>%(desc)s</div></td>
427     <td colspan=‘5‘ align=‘center‘><span class="label label-success success">%(status)s</span></td>
428 </tr>
429 """ # variables: (tid, Class, style, desc, status)
430 
431     REPORT_TEST_OUTPUT_TMPL = r"""
432 %(id)s: %(output)s
433 """ # variables: (id, output)
434 
435     # ------------------------------------------------------------------------
436     # ENDING
437     #
438     # 增加返回顶部按钮  --Findyou
439     ENDING_TMPL = """<div id=‘ending‘>&nbsp;</div>
440     <div style=" position:fixed;right:50px; bottom:30px; width:20px; height:20px;cursor:pointer">
441     <a href="#"><span class="glyphicon glyphicon-eject" style = "font-size:30px;" aria-hidden="true">
442     </span></a></div>
443     """
444 
445 # -------------------- The end of the Template class -------------------
446 
447 
448 TestResult = unittest.TestResult
449 
450 class _TestResult(TestResult):
451     # note: _TestResult is a pure representation of results.
452     # It lacks the output and reporting ability compares to unittest._TextTestResult.
453 
454     def __init__(self, verbosity=1):
455         TestResult.__init__(self)
456         self.stdout0 = None
457         self.stderr0 = None
458         self.success_count = 0
459         self.failure_count = 0
460         self.error_count = 0
461         self.verbosity = verbosity
462 
463         # result is a list of result in 4 tuple
464         # (
465         #   result code (0: success; 1: fail; 2: error),
466         #   TestCase object,
467         #   Test output (byte string),
468         #   stack trace,
469         # )
470         self.result = []
471         #增加一个测试通过率 --Findyou
472         self.passrate=float(0)
473 
474 
475     def startTest(self, test):
476         TestResult.startTest(self, test)
477         # just one buffer for both stdout and stderr
478         self.outputBuffer = StringIO.StringIO()
479         stdout_redirector.fp = self.outputBuffer
480         stderr_redirector.fp = self.outputBuffer
481         self.stdout0 = sys.stdout
482         self.stderr0 = sys.stderr
483         sys.stdout = stdout_redirector
484         sys.stderr = stderr_redirector
485 
486 
487     def complete_output(self):
488         """
489         Disconnect output redirection and return buffer.
490         Safe to call multiple times.
491         """
492         if self.stdout0:
493             sys.stdout = self.stdout0
494             sys.stderr = self.stderr0
495             self.stdout0 = None
496             self.stderr0 = None
497         return self.outputBuffer.getvalue()
498 
499 
500     def stopTest(self, test):
501         # Usually one of addSuccess, addError or addFailure would have been called.
502         # But there are some path in unittest that would bypass this.
503         # We must disconnect stdout in stopTest(), which is guaranteed to be called.
504         self.complete_output()
505 
506 
507     def addSuccess(self, test):
508         self.success_count += 1
509         TestResult.addSuccess(self, test)
510         output = self.complete_output()
511         self.result.append((0, test, output, ‘‘))
512         if self.verbosity > 1:
513             sys.stderr.write(ok )
514             sys.stderr.write(str(test))
515             sys.stderr.write(\n)
516         else:
517             sys.stderr.write(.)
518 
519     def addError(self, test, err):
520         self.error_count += 1
521         TestResult.addError(self, test, err)
522         _, _exc_str = self.errors[-1]
523         output = self.complete_output()
524         self.result.append((2, test, output, _exc_str))
525         if self.verbosity > 1:
526             sys.stderr.write(E  )
527             sys.stderr.write(str(test))
528             sys.stderr.write(\n)
529         else:
530             sys.stderr.write(E)
531 
532     def addFailure(self, test, err):
533         self.failure_count += 1
534         TestResult.addFailure(self, test, err)
535         _, _exc_str = self.failures[-1]
536         output = self.complete_output()
537         self.result.append((1, test, output, _exc_str))
538         if self.verbosity > 1:
539             sys.stderr.write(F  )
540             sys.stderr.write(str(test))
541             sys.stderr.write(\n)
542         else:
543             sys.stderr.write(F)
544 
545 
546 class HTMLTestRunner(Template_mixin):
547     """
548     """
549     def __init__(self, stream=sys.stdout, verbosity=1,title=None,description=None,tester=None):
550         self.stream = stream
551         self.verbosity = verbosity
552         if title is None:
553             self.title = self.DEFAULT_TITLE
554         else:
555             self.title = title
556         if description is None:
557             self.description = self.DEFAULT_DESCRIPTION
558         else:
559             self.description = description
560         if tester is None:
561             self.tester = self.DEFAULT_TESTER
562         else:
563             self.tester = tester
564 
565         self.startTime = datetime.datetime.now()
566 
567 
568     def run(self, test):
569         "Run the given test case or test suite."
570         result = _TestResult(self.verbosity)
571         test(result)
572         self.stopTime = datetime.datetime.now()
573         self.generateReport(test, result)
574         print >>sys.stderr, \nTime Elapsed: %s % (self.stopTime-self.startTime)
575         return result
576 
577 
578     def sortResult(self, result_list):
579         # unittest does not seems to run in any particular order.
580         # Here at least we want to group them together by class.
581         rmap = {}
582         classes = []
583         for n,t,o,e in result_list:
584             cls = t.__class__
585             if not rmap.has_key(cls):
586                 rmap[cls] = []
587                 classes.append(cls)
588             rmap[cls].append((n,t,o,e))
589         r = [(cls, rmap[cls]) for cls in classes]
590         return r
591 
592     #替换测试结果status为通过率 --Findyou
593     def getReportAttributes(self, result):
594         """
595         Return report attributes as a list of (name, value).
596         Override this to add custom attributes.
597         """
598         startTime = str(self.startTime)[:19]
599         duration = str(self.stopTime - self.startTime)
600         status = []
601         status.append(共 %s % (result.success_count + result.failure_count + result.error_count))
602         if result.success_count: status.append(通过 %s    % result.success_count)
603         if result.failure_count: status.append(失败 %s % result.failure_count)
604         if result.error_count:   status.append(错误 %s   % result.error_count  )
605         if status:
606             status = .join(status)
607             self.passrate = str("%.2f%%" % (float(result.success_count) / float(result.success_count + result.failure_count + result.error_count) * 100))
608         else:
609             status = none
610         return [
611             (u测试人员, self.tester),
612             (u开始时间,startTime),
613             (u合计耗时,duration),
614             (u测试结果,status + ",通过率= "+self.passrate),
615         ]
616 
617 
618     def generateReport(self, test, result):
619         report_attrs = self.getReportAttributes(result)
620         generator = HTMLTestRunner %s % __version__
621         stylesheet = self._generate_stylesheet()
622         heading = self._generate_heading(report_attrs)
623         report = self._generate_report(result)
624         ending = self._generate_ending()
625         output = self.HTML_TMPL % dict(
626             title = saxutils.escape(self.title),
627             generator = generator,
628             stylesheet = stylesheet,
629             heading = heading,
630             report = report,
631             ending = ending,
632         )
633         self.stream.write(output.encode(utf8))
634 
635 
636     def _generate_stylesheet(self):
637         return self.STYLESHEET_TMPL
638 
639     #增加Tester显示 -Findyou
640     def _generate_heading(self, report_attrs):
641         a_lines = []
642         for name, value in report_attrs:
643             line = self.HEADING_ATTRIBUTE_TMPL % dict(
644                     name = saxutils.escape(name),
645                     value = saxutils.escape(value),
646                 )
647             a_lines.append(line)
648         heading = self.HEADING_TMPL % dict(
649             title = saxutils.escape(self.title),
650             parameters = ‘‘.join(a_lines),
651             description = saxutils.escape(self.description),
652             tester= saxutils.escape(self.tester),
653         )
654         return heading
655 
656     #生成报告  --Findyou添加注释
657     def _generate_report(self, result):
658         rows = []
659         sortedResult = self.sortResult(result.result)
660         for cid, (cls, cls_results) in enumerate(sortedResult):
661             # subtotal for a class
662             np = nf = ne = 0
663             for n,t,o,e in cls_results:
664                 if n == 0: np += 1
665                 elif n == 1: nf += 1
666                 else: ne += 1
667 
668             # format class description
669             if cls.__module__ == "__main__":
670                 name = cls.__name__
671             else:
672                 name = "%s.%s" % (cls.__module__, cls.__name__)
673             doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
674             desc = doc and %s: %s % (name, doc) or name
675 
676             row = self.REPORT_CLASS_TMPL % dict(
677                 style = ne > 0 and errorClass or nf > 0 and failClass or passClass,
678                 desc = desc,
679                 count = np+nf+ne,
680                 Pass = np,
681                 fail = nf,
682                 error = ne,
683                 cid = c%s % (cid+1),
684             )
685             rows.append(row)
686 
687             for tid, (n,t,o,e) in enumerate(cls_results):
688                 self._generate_report_test(rows, cid, tid, n, t, o, e)
689 
690         report = self.REPORT_TMPL % dict(
691             test_list = ‘‘.join(rows),
692             count = str(result.success_count+result.failure_count+result.error_count),
693             Pass = str(result.success_count),
694             fail = str(result.failure_count),
695             error = str(result.error_count),
696             passrate =self.passrate,
697         )
698         return report
699 
700 
701     def _generate_report_test(self, rows, cid, tid, n, t, o, e):
702         # e.g. ‘pt1.1‘, ‘ft1.1‘, etc
703         has_output = bool(o or e)
704         # ID修改点为下划线,支持Bootstrap折叠展开特效 - Findyou
705         tid = (n == 0 and p or f) + t%s_%s % (cid+1,tid+1)
706         name = t.id().split(.)[-1]
707         doc = t.shortDescription() or ""
708         desc = doc and (%s: %s % (name, doc)) or name
709         tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
710 
711         # utf-8 支持中文 - Findyou
712          # o and e should be byte string because they are collected from stdout and stderr?
713         if isinstance(o, str):
714             # TODO: some problem with ‘string_escape‘: it escape \n and mess up formating
715             # uo = unicode(o.encode(‘string_escape‘))
716             # uo = o.decode(‘latin-1‘)
717             uo = o.decode(utf-8)
718         else:
719             uo = o
720         if isinstance(e, str):
721             # TODO: some problem with ‘string_escape‘: it escape \n and mess up formating
722             # ue = unicode(e.encode(‘string_escape‘))
723             # ue = e.decode(‘latin-1‘)
724             ue = e.decode(utf-8)
725         else:
726             ue = e
727 
728         script = self.REPORT_TEST_OUTPUT_TMPL % dict(
729             id = tid,
730             output = saxutils.escape(uo+ue),
731         )
732 
733         row = tmpl % dict(
734             tid = tid,
735             Class = (n == 0 and hiddenRow or none),
736             style = n == 2 and errorCase or (n == 1 and failCase or passCase),
737             desc = desc,
738             script = script,
739             status = self.STATUS[n],
740         )
741         rows.append(row)
742         if not has_output:
743             return
744 
745     def _generate_ending(self):
746         return self.ENDING_TMPL
747 
748 
749 ##############################################################################
750 # Facilities for running tests from the command line
751 ##############################################################################
752 
753 # Note: Reuse unittest.TestProgram to launch test. In the future we may
754 # build our own launcher to support more specific command line
755 # parameters like test title, CSS, etc.
756 class TestProgram(unittest.TestProgram):
757     """
758     A variation of the unittest.TestProgram. Please refer to the base
759     class for command line parameters.
760     """
761     def runTests(self):
762         # Pick HTMLTestRunner as the default test runner.
763         # base class‘s testRunner parameter is not useful because it means
764         # we have to instantiate HTMLTestRunner before we know self.verbosity.
765         if self.testRunner is None:
766             self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
767         unittest.TestProgram.runTests(self)
768 
769 main = TestProgram
770 
771 ##############################################################################
772 # Executing this module from the command line
773 ##############################################################################
774 
775 if __name__ == "__main__":
776     main(module=None)

 效果:

技术分享图片

 

Python2 HTMLTestRunner自动化测试报告美化

标签:oct   future   ||   HERE   ica   output   变换   需要   param   

原文地址:https://www.cnblogs.com/wangyinghao/p/10314887.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!