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