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测试报告