HTMLTESTRunner自动化测试报告增加截图功能

我们都知道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,‘&amp;‘);
264     s = s.replace(/</g,‘&lt;‘);
265     s = s.replace(/>/g,‘&gt;‘);
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‘>&nbsp;</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

HTMLTESTRunner自动化测试报告增加截图功能的相关文章

Selenium2学习-023-WebUI自动化实战实例-021-获取浏览器显示区域大小,通过 WebDriver 截图功能

之前的博文 020-JavaScript 在 Selenium 自动化中的应用实例之二(获取浏览器显示区域大小) 简述了通过 JavaScript 获取浏览器显示区域大小,此文将简述另一种获取浏览器显示区域大小的方法,即通过 WebDriver 的截图功能,获取显示区域的截图,通过截图的大小获取浏览器显示区域的大小. 直接上码了,敬请小主们参阅,若有不足之处,敬请大神指正,不胜感激! 1 /** 2 * Get width and height about display screen area

Python+Selenium----使用HTMLTestRunner.py生成自动化测试报告2(使用PyCharm )

1.说明 在我前一篇文件(Python+Selenium----使用HTMLTestRunner.py生成自动化测试报告1(使用IDLE ))中简单的写明了,如何生产测试报告,但是使用IDLE很麻烦,而且在实际的项目中也不方便,所以,查了很多资料来研究如何在PyCharm中生成测试报告,故此记录一下(命名什么的不规范就不要纠结了). 2.步骤 第一步:下载HTMLTestRunner.py 参考:Python+Selenium----使用HTMLTestRunner.py生成自动化测试报告1(使

封装selenium自动化框架中的截图功能

对selenium自带的截图功能进行封装: 以下为封装的代码,自定义一个.py文件即可,图片路径自己设置一个. 1 #coding:utf-8 2 3 class Screen(object): 4 ''' 5 封装的截图类,webdriver自带的get_screenshot_as_file() 6 在使用过程中,注意driver参数的传递 7 ''' 8 def __init__(self, driver): 9 ''' 10 写一个构造函数,有一个参数driver 11 ''' 12 se

Allure自动化测试报告我是这样用的

关于自动化测试报告: 之前用过testNG自带的测试报告.优化过reportNG的测试报告.extentreport.Zreport(大飞总原创),这些是我之前都用过的,也是在去年雯姐和我说过Allure2这个报告不错,一直没时间,正巧最近有用到,接触下发现确实是个神器. Allure(已经有allure2了,小编用的就是allure2),生成的测试报告与上述对比,简直堪称完美!先上个测试报告的图表,给大家直观感受下: 下面让我们一起走进Allure的世界,跟上步伐,相信我这一切并不难 一.po

C#实现网页截图功能

网页截图是很常见的实用功能,今天就为大家共享一个实现浏览器截图的代码,主要程序代码如下所示: private void Form_Load(object sender, EventArgs e) { //接收web url string colle = string.Empty; string url = string.Empty; //获取进程调用传入的命令 string[] args = Environment.GetCommandLineArgs(); string[] args = ne

博客园个人博客增加打赏功能心得

1.收款码1.1 保存把自己的支付宝和微信二维码收款码(注意是收款码而不是付款码,别搞错)两个图片保存到电脑,并使用图片编辑工具(Windows自带画图软件,或者QQ等其他截图工具都可以)只截取二维码部分即可. 1.2 上传进入个人博客,点“管理”,再点“相册”,添加好相册名称(类似文件夹名称),并上传这两个收款二维码,然后可以在图片上面右击鼠标查看或复制图片网址. 2.JS2.1 开通博客JS权限点“设置”,找到“博客侧边栏公告(支持HTML代码)”,点击[申请JS权限],这里需要填写理由,一

canvas与html5实现视频截图功能

这段时间一直在研究canvas,突发奇想想做一个可以截屏视频的功能,然后把图片拉去做表情包,哈哈哈哈哈哈~~ 制作方法: 1.在页面中加载视频 在使用canvas制作这个截图功能时,首先必须保证页面上已经加载完成了这个视频,这样才能够方便的对其操作.如果使用下面直接嵌入<video>标签的方式: 1 2 3 4 5 <video loop controls id="testmp4" width="500" height="400"

安卓开发_实现截图功能

4-17号在360云盘提供了 群雄兵法APP的下载,将其作为测试版,在短短10天内便下载量超过1000,虽说不是很高,但是也证明我一个月的努力是值得的. 这两天游戏更新了,我也将推出最新的版本,在原来的测试版本的基础上,修改了一些内容的错误,优化了部分界面,添加了武将生平介绍,武将成长值等资料. 因为广大的用户提出装备模拟之后无法保存的问题,我在自己所学的基础上进行的解决,但是无奈没有解决成功,于是乎先用截图功能来实现装备模拟后的属性保存吧. 网上百度了很多,大部分相当复杂,对于我这个水平来说,

截图功能的简单实现

//截图功能的实现 //1.建立一个bitmapContext UIGraphicsBeginImageContext(self.view.bounds.size); //2.获取bitmapContext CGContextRef context = UIGraphicsGetCurrentContext(); //3.把self.view.layer图层的全部内容渲染到bitmapContext上去. //截取当前图层的一部分 UIRectClip(CGRectMake(100, 100,