标签: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,‘&‘); 288 s = s.replace(/</g,‘<‘); 289 s = s.replace(/>/g,‘>‘); 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‘> </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