zoukankan      html  css  js  c++  java
  • Python的unittest拓展和HTMLReport SKIP报表扩展

    C:Python27Lib中修改unittest内容 

    unittest 在init中添加Myskip代码:

    __all__ = ['TestResult', 'TestCase', 'TestSuite',
               'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main',
               'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless',
               'expectedFailure', 'TextTestResult', 'installHandler',
               'registerResult', 'removeResult', 'removeHandler','Myskip']
    
    from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf, Myskip,skipUnless, expectedFailure)

    unittest在case.py中增加如下代码:

    def _id(obj):
      return obj

    def Myskip(func):
        
        def RebackTest(self):
            if self._resultForDoCleanups.failures or self._resultForDoCleanups.errors:
                raise SkipTest("{} do not excute because {} is failed".format(func.__name__,self._resultForDoCleanups.failures[0][0]._testMethodName))
            func(self)
        return  RebackTest

    C:Python27Lib修改HTMLReport文件(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"
     69 __version__ = "0.8.2"
     70  
     71  
     72 """
     73 Change History
     74  
     75 Version 0.8.2
     76 * Show output inline instead of popup window (Viorel Lupu).
     77  
     78 Version in 0.8.1
     79 * Validated XHTML (Wolfgang Borgert).
     80 * Added description of test classes and test cases.
     81  
     82 Version in 0.8.0
     83 * Define Template_mixin class for customization.
     84 * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
     85  
     86 Version in 0.7.1
     87 * Back port to Python 2.3 (Frank Horowitz).
     88 * Fix missing scroll bars in detail log (Podi).
     89 """
     90  
     91 # TODO: color stderr
     92 # TODO: simplify javascript using ,ore than 1 class in the class attribute?
     93 #coding=utf-8
     94 import datetime
     95 import io
     96 import sys
     97 reload(sys)
     98 sys.setdefaultencoding('utf8')
     99 import time
    100 import unittest
    101 import re
    102 from xml.sax import saxutils
    103  
    104  
    105 # ------------------------------------------------------------------------
    106 # The redirectors below are used to capture output during testing. Output
    107 # sent to sys.stdout and sys.stderr are automatically captured. However
    108 # in some cases sys.stdout is already cached before HTMLTestRunner is
    109 # invoked (e.g. calling logging.basicConfig). In order to capture those
    110 # output, use the redirectors for the cached stream.
    111 #
    112 # e.g.
    113 #   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
    114 #   >>>
    115  
    116 class OutputRedirector(object):
    117     """ Wrapper to redirect stdout or stderr """
    118     def __init__(self, fp):
    119         self.fp = fp
    120  
    121     def write(self, s):
    122         self.fp.write(s)
    123  
    124     def writelines(self, lines):
    125         self.fp.writelines(lines)
    126  
    127     def flush(self):
    128         self.fp.flush()
    129  
    130 stdout_redirector = OutputRedirector(sys.stdout)
    131 stderr_redirector = OutputRedirector(sys.stderr)
    132  
    133  
    134  
    135 # ----------------------------------------------------------------------
    136 # Template
    137  
    138 class Template_mixin(object):
    139     """
    140     Define a HTML template for report customerization and generation.
    141  
    142     Overall structure of an HTML report
    143  
    144     HTML
    145     +------------------------+
    146     |<html>                  |
    147     |  <head>                |
    148     |                        |
    149     |   STYLESHEET           |
    150     |   +----------------+   |
    151     |   |                |   |
    152     |   +----------------+   |
    153     |                        |
    154     |  </head>               |
    155     |                        |
    156     |  <body>                |
    157     |                        |
    158     |   HEADING              |
    159     |   +----------------+   |
    160     |   |                |   |
    161     |   +----------------+   |
    162     |                        |
    163     |   REPORT               |
    164     |   +----------------+   |
    165     |   |                |   |
    166     |   +----------------+   |
    167     |                        |
    168     |   ENDING               |
    169     |   +----------------+   |
    170     |   |                |   |
    171     |   +----------------+   |
    172     |                        |
    173     |  </body>               |
    174     |</html>                 |
    175     +------------------------+
    176     """
    177  
    178     STATUS = {
    179     0: 'pass',
    180     1: 'fail',
    181     2: 'error',
    182     3:'skip'
    183     }
    184  
    185     DEFAULT_TITLE = 'Unit Test Report'
    186     DEFAULT_DESCRIPTION = ''
    187  
    188     # ------------------------------------------------------------------------
    189     # HTML Template
    190  
    191     HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
    192 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    193 <html xmlns="http://www.w3.org/1999/xhtml">
    194 <head>
    195     <title>%(title)s</title>
    196     <meta name="generator" content="%(generator)s"/>
    197     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    198     %(stylesheet)s
    199 </head>
    200 <body>
    201 <script language="javascript" type="text/javascript"><!--
    202 output_list = Array();
    203  
    204 /* level - 0:Summary; 1:Failed; 2:All */
    205 function showCase(level) {
    206     trs = document.getElementsByTagName("tr");
    207     for (var i = 0; i < trs.length; i++) {
    208         tr = trs[i];
    209         id = tr.id;
    210         if (id.substr(0,2) == 'ft') {
    211             if (level < 1) {
    212                 tr.className = 'hiddenRow';
    213             }
    214             else {
    215                 tr.className = '';
    216             }
    217         }
    218         if (id.substr(0,2) == 'pt') {
    219             if (level > 1) {
    220                 tr.className = '';
    221             }
    222             else {
    223                 tr.className = 'hiddenRow';
    224             }        }        if (id.substr(0,2) == 'st') {           if (level > 1) {               tr.className = '';            }           else {               tr.className = 'hiddenRow';           }
    225         }
    226     }
    227 }
    228  
    229 function showClassDetail(cid, count) {
    230     var id_list = Array(count);
    231     var toHide = 1;
    232     for (var i = 0; i < count; i++) {
    233         tid0 = 't' + cid.substr(1) + '.' + (i+1);
    234         tid = 'f' + tid0;
    235         tr = document.getElementById(tid);
    236         if (!tr) {
    237             tid = 'p' + tid0;
    238             tr = document.getElementById(tid);
    239         }
    240          if (!tr) {
    241             tid = 's' + tid0;
    242             tr = document.getElementById(tid);
    243         }
    244         id_list[i] = tid;
    245         if (tr.className) {
    246             toHide = 0;
    247         }
    248  
    249     }
    250     for (var i = 0; i < count; i++) {
    251         tid = id_list[i];
    252         if (toHide) {
    253             document.getElementById('div_'+tid).style.display = 'none'
    254             document.getElementById(tid).className = 'hiddenRow';
    255         }
    256         else {
    257             document.getElementById(tid).className = '';
    258         }
    259     }
    260 }
    261  
    262  
    263 function showTestDetail(div_id){
    264     var details_div = document.getElementById(div_id)
    265     var displayState = details_div.style.display
    266     // alert(displayState)
    267     if (displayState != 'block' ) {
    268         displayState = 'block'
    269         details_div.style.display = 'block'
    270     }
    271     else {
    272         details_div.style.display = 'none'
    273     }
    274 }
    275  
    276  
    277 function html_escape(s) {
    278     s = s.replace(/&/g,'&');
    279     s = s.replace(/</g,'<');
    280     s = s.replace(/>/g,'>');
    281     return s;
    282 }
    283  
    284 /* obsoleted by detail in <div>
    285 function showOutput(id, name) {
    286     var w = window.open("", //url
    287                     name,
    288                     "resizable,scrollbars,status,width=800,height=450");
    289     d = w.document;
    290     d.write("<pre>");
    291     d.write(html_escape(output_list[id]));
    292     d.write("
    ");
    293     d.write("<a href='javascript:window.close()'>close</a>
    ");
    294     d.write("</pre>
    ");
    295     d.close();
    296 }
    297 */
    298 --></script>
    299  
    300 %(heading)s
    301 %(report)s
    302 %(ending)s
    303  
    304 </body>
    305 </html>
    306 """
    307     # variables: (title, generator, stylesheet, heading, report, ending)
    308  
    309  
    310     # ------------------------------------------------------------------------
    311     # Stylesheet
    312     #
    313     # alternatively use a <link> for external style sheet, e.g.
    314     #   <link rel="stylesheet" href="$url" type="text/css">
    315  
    316     STYLESHEET_TMPL = """
    317 <style type="text/css" media="screen">
    318 body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
    319 table       { font-size: 100%; }
    320 pre         { word-wrap:break-word;word-break:break-all;overflow:auto;}
    321  
    322 /* -- heading ---------------------------------------------------------------------- */
    323 h1 {
    324     font-size: 16pt;
    325     color: gray;
    326 }
    327 .heading {
    328     margin-top: 0ex;
    329     margin-bottom: 1ex;
    330 }
    331  
    332 .heading .attribute {
    333     margin-top: 1ex;
    334     margin-bottom: 0;
    335 }
    336  
    337 .heading .description {
    338     margin-top: 4ex;
    339     margin-bottom: 6ex;
    340 }
    341  
    342 /* -- css div popup ------------------------------------------------------------------------ */
    343 a.popup_link {
    344 }
    345  
    346 a.popup_link:hover {
    347     color: red;
    348 }
    349  
    350 .popup_window {
    351     display: none;
    352     position: relative;
    353     left: 0px;
    354     top: 0px;
    355     /*border: solid #627173 1px; */
    356     padding: 10px;
    357     background-color: 00;
    358     font-family: "Lucida Console", "Courier New", Courier, monospace;
    359     text-align: left;
    360     font-size: 8pt;
    361      600px;
    362 }
    363  
    364 }
    365 /* -- report ------------------------------------------------------------------------ */
    366 #show_detail_line {
    367     margin-top: 3ex;
    368     margin-bottom: 1ex;
    369 }
    370 #result_table {
    371      80%;
    372     border-collapse: collapse;
    373     border: 1px solid #777;
    374 }
    375 #header_row {
    376     font-weight: bold;
    377     color: white;
    378     background-color: #777;
    379 }
    380 #result_table td {
    381     border: 1px solid #777;
    382     padding: 2px;
    383 }
    384 #total_row  { font-weight: bold; }
    385 .passClass  { background-color: #6c6; }
    386 .failClass  { background-color: #c60; }
    387 .errorClass { background-color: #c00; }
    388 .passCase   { color: #6c6; }
    389 .failCase   { color: #c60; font-weight: bold; }
    390 .errorCase  { color: #c00; font-weight: bold; }
    391 .hiddenRow  { display: none; }
    392 .testcase   { margin-left: 2em; }
    393  
    394  
    395 /* -- ending ---------------------------------------------------------------------- */
    396 #ending {
    397 }
    398  
    399 </style>
    400 """
    401  
    402  
    403  
    404     # ------------------------------------------------------------------------
    405     # Heading
    406     #
    407  
    408     HEADING_TMPL = """<div class='heading'>
    409 <h1>%(title)s</h1>
    410 %(parameters)s
    411 <p class='description'>%(description)s</p>
    412 </div>
    413  
    414 """ # variables: (title, parameters, description)
    415  
    416     HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
    417 """ # variables: (name, value)
    418  
    419  
    420  
    421     # ------------------------------------------------------------------------
    422     # Report
    423     #
    424  
    425     REPORT_TMPL = """
    426 <p id='show_detail_line'>Show
    427 <a href='javascript:showCase(0)'>Summary</a>
    428 <a href='javascript:showCase(1)'>Failed</a>
    429 <a href='javascript:showCase(2)'>All</a>
    430 </p>
    431 <table id='result_table'>
    432 <colgroup>
    433 <col align='left' />
    434 <col align='right' />
    435 <col align='right' />
    436 <col align='right' />
    437 <col align='right' />
    438 <col align='right' />
    439 </colgroup>
    440 <tr id='header_row'>
    441     <td>Test Group/Test case</td>
    442     <td>Count</td>
    443     <td>Pass</td>
    444     <td>Fail</td>
    445     <td>Error</td>
    446     <td>Skip</td>
    447     <td>View</td>
    448     <td>Screenshot</td>
    449 </tr>
    450 %(test_list)s
    451 <tr id='total_row'>
    452     <td>Total</td>
    453     <td>%(count)s</td>
    454     <td>%(Pass)s</td>
    455     <td>%(fail)s</td>
    456     <td>%(error)s</td>
    457     <td>%(skip)s</td>
    458     <td> </td>
    459     <td> </td>
    460  
    461 </tr>
    462 </table>
    463 """ # variables: (test_list, count, Pass, fail, error)
    464  
    465     REPORT_CLASS_TMPL = r"""
    466 <tr class='%(style)s'>
    467     <td>%(desc)s</td>
    468     <td>%(count)s</td>
    469     <td>%(Pass)s</td>
    470     <td>%(fail)s</td>
    471     <td>%(error)s</td>
    472     <td>%(skip)s</td>
    473     <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
    474     <td> </td>
    475 </tr>
    476 """ # variables: (style, desc, count, Pass, fail,skip, error, cid)
    477  
    478  
    479     REPORT_TEST_WITH_OUTPUT_TMPL = r"""
    480 <tr id='%(tid)s' class='%(Class)s'>
    481     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    482     <td colspan='6' align='center'>
    483     <!--css div popup start-->
    484     <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
    485         %(status)s</a>
    486  
    487     <div id='div_%(tid)s' class="popup_window" >
    488         <div style='text-align: right; color:red;cursor:pointer'>
    489         <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
    490            [x]</a>
    491         </div>
    492         <pre>
    493         %(script)s
    494         </pre>
    495     </div>
    496     <!--css div popup end-->
    497     </td>
    498     <td align='center'>
    499     <a  %(hidde)s  href="%(image)s">picture_shot</a>
    500     </td>
    501 </tr>
    502 """ # variables: (tid, Class, style, desc, status)
    503     REPORT_TEST_NO_OUTPUT_TMPL = r"""
    504 <tr id='%(tid)s' class='%(Class)s'>
    505     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    506     <td colspan='6' align='center'>%(status)s</td>
    507     <td align='center'>
    508     <a  %(hidde)s  href="%(image)s">picture_shot</a>
    509     </td>
    510 </tr>
    511 """ # variables: (tid, Class, style, desc, status)
    512  
    513  
    514     REPORT_TEST_OUTPUT_TMPL = r"""
    515 %(id)s: %(output)s
    516 """
    517      
    518     # variables: (id, output)
    519  
    520  
    521  
    522     # ------------------------------------------------------------------------
    523     # ENDING
    524     #
    525  
    526     ENDING_TMPL = """<div id='ending'> </div>"""
    527  
    528 # -------------------- The end of the Template class -------------------
    529  
    530  
    531 TestResult = unittest.TestResult
    532  
    533 class _TestResult(TestResult):
    534     # note: _TestResult is a pure representation of results.
    535     # It lacks the output and reporting ability compares to unittest._TextTestResult.
    536  
    537     def __init__(self, verbosity=1):
    538         TestResult.__init__(self)
    539         self.stdout0 = None
    540         self.stderr0 = None
    541         self.success_count = 0
    542         self.skipped_count=0#add skipped_count
    543         self.failure_count = 0
    544         self.error_count = 0
    545         self.verbosity = verbosity
    546  
    547         # result is a list of result in 4 tuple
    548         # (
    549         #   result code (0: success; 1: fail; 2: error),
    550         #   TestCase object,
    551         #   Test output (byte string),
    552         #   stack trace,
    553         # )
    554         self.result = []
    555  
    556  
    557     def startTest(self, test):
    558         TestResult.startTest(self, test)
    559         # just one buffer for both stdout and stderr
    560         self.outputBuffer = io.BytesIO()
    561         stdout_redirector.fp = self.outputBuffer
    562         stderr_redirector.fp = self.outputBuffer
    563         self.stdout0 = sys.stdout
    564         self.stderr0 = sys.stderr
    565         sys.stdout = stdout_redirector
    566         sys.stderr = stderr_redirector
    567  
    568  
    569     def complete_output(self):
    570         """
    571         Disconnect output redirection and return buffer.
    572         Safe to call multiple times.
    573         """
    574         if self.stdout0:
    575             sys.stdout = self.stdout0
    576             sys.stderr = self.stderr0
    577             self.stdout0 = None
    578             self.stderr0 = None
    579         return self.outputBuffer.getvalue()
    580  
    581  
    582     def stopTest(self, test):
    583         # Usually one of addSuccess, addError or addFailure would have been called.
    584         # But there are some path in unittest that would bypass this.
    585         # We must disconnect stdout in stopTest(), which is guaranteed to be called.
    586         self.complete_output()
    587  
    588  
    589     def addSuccess(self, test):
    590         self.success_count += 1
    591         TestResult.addSuccess(self, test)
    592         output = self.complete_output()
    593         self.result.append((0, test, output, ''))
    594         if self.verbosity > 1:
    595             sys.stderr.write('ok ')
    596             sys.stderr.write(str(test))
    597             sys.stderr.write('
    ')
    598         else:
    599             sys.stderr.write('.')
    600  
    601     def addSkip(self, test, reason):
    602         self.skipped_count+= 1
    603         TestResult.addSkip(self, test,reason)
    604         output = self.complete_output()
    605         self.result.append((3, test,'',reason))
    606         if self.verbosity > 1:
    607             sys.stderr.write('skip ')
    608             sys.stderr.write(str(test))
    609             sys.stderr.write('
    ')
    610         else:
    611             sys.stderr.write('s')
    612     def addError(self, test, err):
    613         self.error_count += 1
    614         TestResult.addError(self, test, err)
    615         _, _exc_str = self.errors[-1]
    616         output = self.complete_output()
    617         self.result.append((2, test, output, _exc_str))
    618         if self.verbosity > 1:
    619             sys.stderr.write('E  ')
    620             sys.stderr.write(str(test))
    621             sys.stderr.write('
    ')
    622         else:
    623             sys.stderr.write('E')
    624  
    625     def addFailure(self, test, err):
    626         self.failure_count += 1
    627         TestResult.addFailure(self, test, err)
    628         _, _exc_str = self.failures[-1]
    629         output = self.complete_output()
    630         self.result.append((1, test, output, _exc_str))
    631         if self.verbosity > 1:
    632             sys.stderr.write('F  ')
    633             sys.stderr.write(str(test))
    634             sys.stderr.write('
    ')
    635         else:
    636             sys.stderr.write('F')
    637  
    638  
    639 class HTMLTestRunner(Template_mixin):
    640     """
    641     """
    642     def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None,name=None):
    643         self.stream = stream
    644         self.verbosity = verbosity
    645         if title is None:
    646             self.title = self.DEFAULT_TITLE
    647         else:
    648             self.title = title
    649         if name is None:
    650             self.name =''
    651         else:
    652             self.name = name
    653         if description is None:
    654             self.description = self.DEFAULT_DESCRIPTION
    655         else:
    656             self.description = description
    657  
    658         self.startTime = datetime.datetime.now()
    659  
    660  
    661     def run(self, test):
    662         "Run the given test case or test suite."
    663         result = _TestResult(self.verbosity)
    664         test(result)
    665         self.stopTime = datetime.datetime.now()
    666         self.generateReport(test, result)
    667         # print (sys.stderr, '
    Time Elapsed: %s' % (self.stopTime-self.startTime))
    668         return result
    669  
    670  
    671     def sortResult(self, result_list):
    672         # unittest does not seems to run in any particular order.
    673         # Here at least we want to group them together by class.
    674         rmap = {}
    675         classes = []
    676         for n,t,o,e in result_list:
    677             cls = t.__class__
    678             if not cls in rmap:
    679                 rmap[cls] = []
    680                 classes.append(cls)
    681             rmap[cls].append((n,t,o,e))
    682         r = [(cls, rmap[cls]) for cls in classes]
    683         return r
    684  
    685  
    686     def getReportAttributes(self, result):
    687         """
    688         Return report attributes as a list of (name, value).
    689         Override this to add custom attributes.
    690         """
    691         startTime = str(self.startTime)[:19]
    692         duration = str(self.stopTime - self.startTime)
    693         status = []
    694         if result.success_count: status.append('Pass %s'    % result.success_count)
    695         if result.failure_count: status.append('Failure %s' % result.failure_count)
    696         if result.skipped_count: status.append('Skip %s' % result.skipped_count)
    697         if result.error_count:   status.append('Error %s'   % result.error_count  )
    698         if status:
    699             status = ' '.join(status)
    700         else:
    701             status = 'none'
    702         return [
    703             ('Start Time', startTime),
    704             ('Duration', duration),
    705             ('Status', status),
    706         ]
    707  
    708  
    709     def generateReport(self, test, result):
    710         report_attrs = self.getReportAttributes(result)#报告的头部
    711         generator = 'HTMLTestRunner %s' % __version__
    712         stylesheet = self._generate_stylesheet()#拿到css文件
    713         heading = self._generate_heading(report_attrs)
    714         report = self._generate_report(result)
    715         ending = self._generate_ending()
    716         output = self.HTML_TMPL % dict(
    717             title = saxutils.escape(self.title),
    718             generator = generator,
    719             stylesheet = stylesheet,
    720             heading = heading,
    721             report = report,
    722             ending = ending,
    723         )
    724         self.stream.write(output.encode('utf8'))
    725  
    726  
    727     def _generate_stylesheet(self):
    728         return self.STYLESHEET_TMPL
    729  
    730  
    731     def _generate_heading(self, report_attrs):
    732         a_lines = []
    733         for name, value in report_attrs:
    734             line = self.HEADING_ATTRIBUTE_TMPL % dict(
    735                     name = saxutils.escape(name),
    736                     value = saxutils.escape(value),
    737                 )
    738             a_lines.append(line)
    739         heading = self.HEADING_TMPL % dict(
    740             title = saxutils.escape(self.title),
    741             parameters = ''.join(a_lines),
    742             description = saxutils.escape(self.description),
    743         )
    744         return heading
    745 #根据result收集报告
    746     def _generate_report(self, result):
    747         rows = []
    748         sortedResult = self.sortResult(result.result)
    749         i = 0
    750         for cid, (cls, cls_results) in enumerate(sortedResult):
    751             # subtotal for a class
    752             np = nf =ns=ne = 0#np代表pass个数,nf代表fail,ns代表skip,ne,代表error
    753             for n,t,o,e in cls_results:
    754                 if n == 0: np += 1
    755                 elif n == 1: nf += 1
    756                 elif n==3:ns+=1
    757                 else: ne += 1
    758  
    759             # format class description
    760             # if cls.__module__ == "__main__":
    761             #     name = cls.__name__
    762             # else:
    763             #     name = "%s.%s" % (cls.__module__, cls.__name__)
    764             name = cls.__name__
    765             try:
    766                 core_name=self.name[i]
    767             except Exception,e:
    768                 core_name =''
    769             # doc = (cls.__doc__)+core_name and (cls.__doc__+core_name).split("
    ")[0] or ""
    770             doc = (cls.__doc__)  and cls.__doc__ .split("
    ")[0] or ""
    771             desc = doc and '%s: %s' % (name, doc) or name
    772             i=i+1            #生成每个TestCase类的汇总数据,对于报告中的
    773             row = self.REPORT_CLASS_TMPL % dict(
    774                 style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
    775                 desc = desc,
    776                 count = np+nf+ne+ns,
    777                 Pass = np,
    778                 fail = nf,
    779                 error = ne,
    780                 skip=ns,
    781                 cid = 'c%s' % (cid+1),
    782             )
    783             rows.append(row)
    784             #生成每个TestCase类中所有方法的测试结果
    785             for tid, (n,t,o,e) in enumerate(cls_results):
    786                 self._generate_report_test(rows, cid, tid, n, t, o, e)
    787  
    788         report = self.REPORT_TMPL % dict(
    789             test_list = ''.join(rows),
    790             count = str(result.success_count+result.failure_count+result.error_count+result.skipped_count),
    791             Pass = str(result.success_count),
    792             fail = str(result.failure_count),
    793             error = str(result.error_count),
    794             skip=str(result.skipped_count)
    795         )
    796         return report
    797  
    798  
    799     def _generate_report_test(self, rows, cid, tid, n, t, o, e):
    800         # e.g. 'pt1.1', 'ft1.1', etc
    801         has_output = bool(o or e)
    802         tid = (n == 0 and 'p' or n==3 and 's' or 'f') + 't%s.%s' % (cid+1,tid+1)
    803         name = t.id().split('.')[-1]
    804         doc = t.shortDescription() or ""
    805         desc = doc and ('%s: %s' % (name, doc)) or name
    806         tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
    807         uo1=""
    808         # o and e should be byte string because they are collected from stdout and stderr?
    809         if isinstance(o,str):
    810             uo = str(o)
    811         else:
    812             uo = e
    813         if isinstance(e,str):
    814             # TODO: some problem with 'string_escape': it escape 
     and mess up formating
    815             # ue = unicode(e.encode('string_escape'))
    816             ue = e
    817         else:
    818             ue = o
    819         script = self.REPORT_TEST_OUTPUT_TMPL % dict(
    820             id = tid,
    821             output = saxutils.escape(str(uo) + str(ue))
    822         )
    823         
    824         if "shot_picture_name" in str(saxutils.escape(str(ue))):
    825             hidde_status=''
    826             pattern = re.compile(r'AssertionError:.*?shot_picture_name=(.*)',re.S)
    827             shot_name =re.search(pattern,str(saxutils.escape(str(e))))
    828             try:
    829                 image_url="http://192.168.99.105/contractreport/screenshot/"+time.strftime("%Y-%m-%d", time.localtime(time.time()))+"/"+shot_name.group(1)+".png"
    830             except Exception,e:
    831                 image_url = "http://192.168.99.105/contractreport/screenshot/" + time.strftime("%Y-%m-%d",time.localtime(time.time()))
    832  
    833         else:
    834             hidde_status = '''hidden="hidden"'''
    835             image_url=''
    836         row = tmpl % dict(
    837             tid = tid,
    838             Class = (n == 0 and 'hiddenRow' or 'none'),
    839             style=n == 2 and 'errorCase' or (n == 1 and 'failCase') or (n == 3 and 'skipCase' or 'none'),
    840             desc = desc,
    841             script = script,
    842             hidde=hidde_status,
    843         image=image_url,
    844             status = self.STATUS[n],
    845         )
    846  
    847         rows.append(row)
    848         if not has_output:
    849             return
    850  
    851     def _generate_ending(self):
    852         return self.ENDING_TMPL
    853  
    854  
    855 ##############################################################################
    856 # Facilities for running tests from the command line
    857 ##############################################################################
    858  
    859 # Note: Reuse unittest.TestProgram to launch test. In the future we may
    860 # build our own launcher to support more specific command line
    861 # parameters like test title, CSS, etc.
    862 # class TestProgram(unittest.TestProgram):
    863 #     """
    864 #     A variation of the unittest.TestProgram. Please refer to the base
    865 #     class for command line parameters.
    866 #     """
    867 #     def runTests(self):
    868 #         # Pick HTMLTestRunner as the default test runner.
    869 #         # base class's testRunner parameter is not useful because it means
    870 #         # we have to instantiate HTMLTestRunner before we know self.verbosity.
    871 #         if self.testRunner is None:
    872 #             self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
    873 #         unittest.TestProgram.runTests(self)
    874 #
    875 # main = TestProgram
    876  
    877 ##############################################################################
    878 # Executing this module from the command line
    879 ##############################################################################
    880  
    881 if __name__ == "__main__":
    882     main(module=None)

    测试SKIP代码

     1 #coding=utf-8
     2 import unittest
     3 # import HTMLTestRunner
     4 import HTMLTestRunner_python2
     5 import time
     6 import sys,os
     7 
     8 class Mydemo(unittest.TestCase):
     9     def test1(self):
    10         print "excute test1"
    11     @unittest.Myskip
    12     def test2(self):
    13         print "excute test2"
    14 
    15     @unittest.Myskip
    16     def test3(self):
    17         print "excute test3"
    18         raise AssertionError("test3 fail")
    19     @unittest.Myskip
    20     def test4(self):
    21         print "excute test4"
    22 
    23 
    24 def suite():
    25     #添加测试用例
    26     suiteTest=unittest.TestSuite()
    27     # suiteTest.addTest(Mydemo("test1"))
    28     # suiteTest.addTest(Mydemo("test2"))
    29     # suiteTest.addTest(Mydemo("test3"))
    30     # suiteTest.addTest(Mydemo("test4"))
    31     suiteTest.addTests(map(Mydemo,["test1","test2","test3","test4"]))
    32     return suiteTest
    33 
    34 if __name__=='__main__':
    35     # unittest.main()
    36     now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
    37     filepath = r'D:Python
    eport{0}result.html'.format(now)
    38     print(filepath)
    39     fp = file(filepath, 'wb')
    40 
    41     # 定义测试报告的标题与描述
    42     runner = HTMLTestRunner_python2.HTMLTestRunner(stream=fp, title=u'单元测试用例', description=u'测试执行情况')
    43     #执行测试用例
    44     runner.run(suite())
    45     fp.close()
    测试结果如图所示:

  • 相关阅读:
    Android Studio使用笔记
    Android Material Design之在RecyclerView中嵌套CardView实现
    RR 和RC 幻读问题
    mysql rr和rc区别
    7.2 Database Backup Methods 数据备份方法:
    7.1 Backup and Recovery Types 备份和恢复类型
    Chapter 7 Backup and Recovery 备份和恢复:
    mysqldump 一些参数体验
    (?m) 可以让.去匹配换行
    perl 正则前导字符
  • 原文地址:https://www.cnblogs.com/yye2010/p/8579686.html
Copyright © 2011-2022 走看看