Python3之HTMLTestRunner测试报告美化

Posted lwjnicole

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python3之HTMLTestRunner测试报告美化相关的知识,希望对你有一定的参考价值。

  前面我们讲到过在做自动化测试或单元测试的时候使用HTMLTestRunner来生成测试报告,并且由于Python2 和 Python3 对于HTMLTestRunner的支持稍微有点差异,所以我们将HTMLTestRunner进行了改造,从而适配Python3,详细改造步骤可以参考:HTMLTestRunner修改成Python3版本

  但是改造后的HTMLTestRunner生成的测试报告不是特别的美观,所以我又对HTMLTestRunner进行了进一步的改造,主要是做一些美化。

  美化之前的测试报告如下:

 

   美化之后的测试报告如下:

   从上面两个报告的对比来看,第二个测试报告是不是更为美观呢?

 

下面是我改造后的HTMLTestRunner源码:

  1 # -*- coding: utf-8 -*-
  2 
  3 """
  4 A TestRunner for use with the Python unit testing framework. It
  5 generates a HTML report to show the result at a glance.
  6 
  7 The simplest way to use this is to invoke its main method. E.g.
  8 
  9     import unittest
 10     import HTMLTestRunner
 11 
 12     ... define your tests ...
 13 
 14     if __name__ == \'__main__\':
 15         HTMLTestRunner.main()
 16 
 17 
 18 For more customization options, instantiates a HTMLTestRunner object.
 19 HTMLTestRunner is a counterpart to unittest\'s TextTestRunner. E.g.
 20 
 21     # output to a file
 22     fp = file(\'my_report.html\', \'wb\')
 23     runner = HTMLTestRunner.HTMLTestRunner(
 24                 stream=fp,
 25                 title=\'My unit test\',
 26                 description=\'This demonstrates the report output by HTMLTestRunner.\'
 27                 )
 28 
 29     # Use an external stylesheet.
 30     # See the Template_mixin class for more customizable options
 31     runner.STYLESHEET_TMPL = \'<link rel="stylesheet" href="my_stylesheet.css" type="text/css">\'
 32 
 33     # run the test
 34     runner.run(my_test_suite)
 35 
 36 
 37 ------------------------------------------------------------------------
 38 Copyright (c) 2004-2007, Wai Yip Tung
 39 All rights reserved.
 40 
 41 Redistribution and use in source and binary forms, with or without
 42 modification, are permitted provided that the following conditions are
 43 met:
 44 
 45 * Redistributions of source code must retain the above copyright notice,
 46   this list of conditions and the following disclaimer.
 47 * Redistributions in binary form must reproduce the above copyright
 48   notice, this list of conditions and the following disclaimer in the
 49   documentation and/or other materials provided with the distribution.
 50 * Neither the name Wai Yip Tung nor the names of its contributors may be
 51   used to endorse or promote products derived from this software without
 52   specific prior written permission.
 53 
 54 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 55 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 56 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 57 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 58 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 59 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 60 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 61 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 62 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 63 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 64 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 65 """
 66 
 67 # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
 68 
 69 __author__ = "Wai Yip Tung"
 70 __version__ = "0.8.2.3"
 71 
 72 
 73 """
 74 Change History
 75 Version 0.8.2.1 -Findyou
 76 * 改为支持python3
 77 
 78 Version 0.8.2.1 -Findyou
 79 * 支持中文,汉化
 80 * 调整样式,美化(需要连入网络,使用的百度的Bootstrap.js)
 81 * 增加 通过分类显示、测试人员、通过率的展示
 82 * 优化“详细”与“收起”状态的变换
 83 * 增加返回顶部的锚点
 84 
 85 Version 0.8.2
 86 * Show output inline instead of popup window (Viorel Lupu).
 87 
 88 Version in 0.8.1
 89 * Validated XHTML (Wolfgang Borgert).
 90 * Added description of test classes and test cases.
 91 
 92 Version in 0.8.0
 93 * Define Template_mixin class for customization.
 94 * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
 95 
 96 Version in 0.7.1
 97 * Back port to Python 2.3 (Frank Horowitz).
 98 * Fix missing scroll bars in detail log (Podi).
 99 """
100 
101 # TODO: color stderr
102 # TODO: simplify javascript using ,ore than 1 class in the class attribute?
103 
104 import datetime
105 import io
106 import sys
107 import time
108 import unittest
109 from xml.sax import saxutils
110 import sys
111 
112 # ------------------------------------------------------------------------
113 # The redirectors below are used to capture output during testing. Output
114 # sent to sys.stdout and sys.stderr are automatically captured. However
115 # in some cases sys.stdout is already cached before HTMLTestRunner is
116 # invoked (e.g. calling logging.basicConfig). In order to capture those
117 # output, use the redirectors for the cached stream.
118 #
119 # e.g.
120 #   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
121 #   >>>
122 
123 class OutputRedirector(object):
124     """ Wrapper to redirect stdout or stderr """
125     def __init__(self, fp):
126         self.fp = fp
127 
128     def write(self, s):
129         self.fp.write(s)
130 
131     def writelines(self, lines):
132         self.fp.writelines(lines)
133 
134     def flush(self):
135         self.fp.flush()
136 
137 stdout_redirector = OutputRedirector(sys.stdout)
138 stderr_redirector = OutputRedirector(sys.stderr)
139 
140 # ----------------------------------------------------------------------
141 # Template
142 
143 class Template_mixin(object):
144     """
145     Define a HTML template for report customerization and generation.
146 
147     Overall structure of an HTML report
148 
149     HTML
150     +------------------------+
151     |<html>                  |
152     |  <head>                |
153     |                        |
154     |   STYLESHEET           |
155     |   +----------------+   |
156     |   |                |   |
157     |   +----------------+   |
158     |                        |
159     |  </head>               |
160     |                        |
161     |  <body>                |
162     |                        |
163     |   HEADING              |
164     |   +----------------+   |
165     |   |                |   |
166     |   +----------------+   |
167     |                        |
168     |   REPORT               |
169     |   +----------------+   |
170     |   |                |   |
171     |   +----------------+   |
172     |                        |
173     |   ENDING               |
174     |   +----------------+   |
175     |   |                |   |
176     |   +----------------+   |
177     |                        |
178     |  </body>               |
179     |</html>                 |
180     +------------------------+
181     """
182 
183     STATUS = {
184     0: \'通过\',
185     1: \'失败\',
186     2: \'错误\',
187     }
188     # 默认测试标题
189     DEFAULT_TITLE = \'API自动化测试报告\'
190     DEFAULT_DESCRIPTION = \'\'
191     # 默认测试人员
192     DEFAULT_TESTER = \'lwjnicole\'
193 
194     # ------------------------------------------------------------------------
195     # HTML Template
196 
197     HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
198 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
199 <html xmlns="http://www.w3.org/1999/xhtml">
200 <head>
201     <title>%(title)s</title>
202     <meta name="generator" content="%(generator)s"/>
203     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
204     <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
205     <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
206     <script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
207     %(stylesheet)s
208 </head>
209 <body >
210 <script language="javascript" type="text/javascript">
211 output_list = Array();
212 
213 /*level 调整增加只显示通过用例的分类 --Adil
214 0:Summary //all hiddenRow
215 1:Failed  //pt hiddenRow, ft none
216 2:Pass    //pt none, ft hiddenRow
217 3:Error   // pt hiddenRow, ft none
218 4:All     //pt none, ft none
219 下面设置 按钮展开逻辑  --Yang Yao Jun
220 */
221 function showCase(level) {
222     trs = document.getElementsByTagName("tr");
223     for (var i = 0; i < trs.length; i++) {
224         tr = trs[i];
225         id = tr.id;
226         if (id.substr(0,2) == \'ft\') {
227             if (level == 2 || level == 0 ) {
228                 tr.className = \'hiddenRow\';
229             }
230             else {
231                 tr.className = \'\';
232             }
233         }
234         if (id.substr(0,2) == \'pt\') {
235             if (level < 2 || level ==3 ) {
236                 tr.className = \'hiddenRow\';
237             }
238             else {
239                 tr.className = \'\';
240             }
241         }
242     }
243 
244     //加入【详细】切换文字变化 --Findyou
245     detail_class=document.getElementsByClassName(\'detail\');
246     //console.log(detail_class.length)
247     if (level == 3) {
248         for (var i = 0; i < detail_class.length; i++){
249             detail_class[i].innerHTML="收起"
250         }
251     }
252     else{
253             for (var i = 0; i < detail_class.length; i++){
254             detail_class[i].innerHTML="详细"
255         }
256     }
257 }
258 
259 function showClassDetail(cid, count) {
260     var id_list = Array(count);
261     var toHide = 1;
262     for (var i = 0; i < count; i++) {
263         //ID修改 点 为 下划线 -Findyou
264         tid0 = \'t\' + cid.substr(1) + \'_\' + (i+1);
265         tid = \'f\' + tid0;
266         tr = document.getElementById(tid);
267         if (!tr) {
268             tid = \'p\' + tid0;
269             tr = document.getElementById(tid);
270         }
271         id_list[i] = tid;
272         if (tr.className) {
273             toHide = 0;
274         }
275     }
276     for (var i = 0; i < count; i++) {
277         tid = id_list[i];
278         //修改点击无法收起的BUG,加入【详细】切换文字变化 --Findyou
279         if (toHide) {
280             document.getElementById(tid).className = \'hiddenRow\';
281             document.getElementById(cid).innerText = "详细"
282         }
283         else {
284             document.getElementById(tid).className = \'\';
285             document.getElementById(cid).innerText = "收起"
286         }
287     }
288 }
289 
290 function html_escape(s) {
291     s = s.replace(/&/g,\'&\');
292     s = s.replace(/</g,\'<\');
293     s = s.replace(/>/g,\'>\');
294     return s;
295 }
296 </script>
297 %(heading)s
298 %(report)s
299 %(ending)s
300 
301 </body>
302 </html>
303 """
304     # variables: (title, generator, stylesheet, heading, report, ending)
305 
306 
307     # ------------------------------------------------------------------------
308     # Stylesheet
309     #
310     # alternatively use a <link> for external style sheet, e.g.
311     #   <link rel="stylesheet" href="$url" type="text/css">
312 
313     STYLESHEET_TMPL = """
314 <style type="text/css" media="screen">
315 body        { font-family: Microsoft YaHei,Tahoma,arial,helvetica,sans-serif;padding: 20px; font-size: 80%; }
316 table       { font-size: 100%; }
317 
318 /* -- heading ---------------------------------------------------------------------- */
319 .heading {
320     margin-top: 0ex;
321     margin-bottom: 1ex;
322 }
323 
324 .heading .description {
325     margin-top: 4ex;
326     margin-bottom: 6ex;
327 }
328 
329 /* -- report ------------------------------------------------------------------------ */
330 #total_row  { font-weight: bold; }
331 .passCase   { color: #5cb85c; }
332 .failCase   { color: #d9534f; font-weight: bold; }
333 .errorCase  { color: #f0ad4e; font-weight: bold; }
334 .hiddenRow  { display: none; }
335 .testcase   { margin-left: 2em; }
336 </style>
337 """
338 
339     # ------------------------------------------------------------------------
340     # Heading
341     #
342 
343     HEADING_TMPL = """<div class=\'heading\'>
344 <h1 style="font-family: Microsoft YaHei">%(title)s</h1>
345 %(parameters)s
346 <p class=\'description\'>%(description)s</p>
347 </div>
348 
349 """ # variables: (title, parameters, description)
350 
351     HEADING_ATTRIBUTE_TMPL = """<p class=\'attribute\'><strong>%(name)s : </strong> %(value)s</p>
352 """ # variables: (name, value)
353 
354 
355 
356     # ------------------------------------------------------------------------
357     # Report
358     #
359     # 汉化,加美化效果 --Yang Yao Jun
360     #
361     # 这里涉及到了 Bootstrap 前端技术,Bootstrap 按钮 资料介绍详见:http://www.runoob.com/bootstrap/bootstrap-buttons.html
362     #
363     REPORT_TMPL = """
364     <p id=\'show_detail_line\'>
365     <a class="btn btn-primary" href=\'javascript:showCase(0)\'>通过率 [%(passrate)s ]</a>
366     <a class="btn btn-success" href=\'javascript:showCase(2)\'>通过[ %(Pass)s ]</a>
367     <a class="btn btn-warning" href=\'javascript:showCase(3)\'>错误[ %(error)s ]</a>
368     <a class="btn btn-danger" href=\'javascript:showCase(1)\'>失败[ %(fail)s ]</a>
369     <a class="btn btn-info" href=\'javascript:showCase(4)\'>所有[ %(count)s ]</a>
370     </p>
371 <table id=\'result_table\' class="table table-condensed table-bordered table-hover">
372 <colgroup>
373 <col align=\'left\' />
374 <col align=\'right\' />
375 <col align=\'right\' />
376 <col align=\'right\' />
377 <col align=\'right\' />
378 <col align=\'right\' />
379 </colgroup>
380 <tr id=\'header_row\' class="text-center success" style="font-weight: bold;font-size: 14px;">
381     <td>用例集/测试用例</td>
382     <td>总计</td>
383     <td>通过</td>
384     <td>错误</td>
385     <td>失败</td>
386     <td>详细</td>
387 </tr>
388 %(test_list)s
389 <tr id=\'total_row\' class="text-center active">
390     <td>总计</td>
391     <td>%(count)s</td>
392     <td>%(Pass)s</td>
393     <td>%(error)s</td>
394     <td>%(fail)s</td>
395     <td>通过率:%(passrate)s</td>
396 </tr>
397 </table>
398 """ # variables: (test_list, count, Pass, fail, error ,passrate)
399 
400     REPORT_CLASS_TMPL = r"""
401 <tr class=\'%(style)s warning\'>
402     <td>%(desc)s</td>
403     <td class="text-center">%(count)s</td>
404     <td class="text-center">%(Pass)s</td>
405     <td class="text-center">%(error)s</td>
406     <td class="text-center">%(fail)s</td>
407     <td class="text-center"><a href="javascript:showClassDetail(\'%(cid)s\',%(count)s)" class="detail" id=\'%(cid)s\'>详细</a></td>
408 </tr>
409 """ # variables: (style, desc, count, Pass, fail, error, cid)
410 
411     #失败 的样式,去掉原来JS效果,美化展示效果  -Findyou
412     REPORT_TEST_WITH_OUTPUT_TMPL = r"""
413 <tr id=\'%(tid)s\' class=\'%(Class)s\'>
414     <td class=\'%(style)s\'><div class=\'testcase\'>%(desc)s</div></td>
415     <td colspan=\'5\' align=\'center\'>
416     <!--默认收起错误信息 -Findyou
417     <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>
418     <div id=\'div_%(tid)s\' class="collapse">  -->
419 
420     <!-- 默认展开错误信息 -Findyou -->
421     <button id=\'btn_%(tid)s\' type="button"  class="btn btn-danger btn-xs" data-toggle="collapse" data-target=\'#div_%(tid)s\'>%(status)s</button>
422     <div id=\'div_%(tid)s\' class="collapse in" style=\'text-align: left; color:red;cursor:pointer\'>
423     <pre>
424     %(script)s
425     </pre>
426     </div>
427     </td>
428 </tr>
429 """ # variables: (tid, Class, style, desc, status)
430 
431     # 通过 的样式,加标签效果  -Findyou
432     REPORT_TEST_NO_OUTPUT_TMPL = r"""
433 <tr id=\'%(tid)s\' class=\'%(Class)s\'>
434     <td class=\'%(style)s\'><div class=\'testcase\'>%(desc)s</div></td>
435     <td colspan=\'5\' align=\'center\'><span class="label label-success success">%(status)s</span></td>
436 </tr>
437 """ # variables: (tid, Class, style, desc, status)
438 
439     REPORT_TEST_OUTPUT_TMPL = r"""
440 %(id)s: %(output)s
441 """ # variables: (id, output)
442 
443     # ------------------------------------------------------------------------
444     # ENDING
445     #
446     # 增加返回顶部按钮  --Findyou
447     ENDING_TMPL = """<div id=\'ending\'> </div>
448     <div style=" position:fixed;right:50px; bottom:30px; width:20px; height:20px;cursor:pointer">
449     <a href="#"><span class="glyphicon glyphicon-eject" style = "font-size:30px;" aria-hidden="true">
450     </span></a></div>
451     """
452 
453 # -------------------- The end of the Template class -------------------
454 
455 
456 TestResult = unittest.TestResult
457 
458 class _TestResult(TestResult):
459     # note: _TestResult is a pure representation of results.
460     # It lacks the output and reporting ability compares to unittest._TextTestResult.
461 
462     def __init__(self, verbosity=1):
463         TestResult.__init__(self)
464         self.stdout0 = None
465         self.stderr0 = None
466         self.success_count = 0
467         self.failure_count = 0
468         self.error_count = 0
469         self.verbosity = verbosity
470 
471        

以上是关于Python3之HTMLTestRunner测试报告美化的主要内容,如果未能解决你的问题,请参考以下文章

python3中用HTMLTestRunner.py报ImportError: No module named 'StringIO'的解决方法:

python3中用HTMLTestRunner.py报ImportError: No module named 'StringIO'解决办法

selenium之百度搜索+有道翻译的简单testcase执行-----用例报告(HTMLTestRunner)

Python3和HTMLTestRunner生成html测试报告

pyhton+selenium+unittest自动化测试框架之测试报告

python3修改HTMLTestRunner,生成有截图的测试报告,并发送测试邮件