zoukankan      html  css  js  c++  java
  • UI自动化框架搭建(三):简单目录层级结构实现(包含ui脚本、工具、报告、驱动)

    简单目录层级分4层(效果见下图)

    driver层:       驱动层,放置各个浏览器驱动版本,做ui自动化需要考虑兼容性(类型是否支持谷歌,火狐,ie等,支持哪几个谷歌版本等等)

    testcases层:   用例层,放置UI自动化脚本,脚本命名一般以test_开头

    report层:        报告层,放置UI自动化运行结果报告,一般以html格式生成

    utils层:        工具层,放置工具类,类似下图中的HTMLTestRunner文件(生成结果报告类),还可以放数据库操作、时间操作、字符串处理、文件处理等等类

    run_all_case.py:  主入口,执行UI自动化,只需要执行这个类,就会去获取所有testcases层的用例,然后运行结果保存在report层

    run_all_case.py:具体代码如下

    
    
    # -*- coding:utf-8 -*-
    import unittest
    import os
    from utils.HTMLTestRunnerForPy3 import HTMLTestRunner
    from datetime import datetime



    if __name__ == "__main__":
    #挑选用例,pattern='test_*.py'表示添加test_开头的py文件
    casePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'testcases')
    discover = unittest.defaultTestLoader.discover(
    start_dir=casePath,
    pattern='test_*.py'
    )

    #指定生成报告地址
    reportPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'reports')
    reportName = datetime.now().strftime("%Y%m%d%H%M%S") + '.html'
    reportFile = os.path.join(reportPath, reportName)
    fp = open(reportFile, 'wb')


    # 运行用例
    runner = HTMLTestRunner(
    stream=fp,
    # 生成的html报告标题
    title='银行UI自动化测试报告',
    # 1是粗略的报告,2是详细的报告
    verbosity=2,
    # 生成的html描述
    description='银行UI自动化测试报告'
    )
    runner.run(discover)
    # 创建的文件都需要关闭
    fp.close()
     

     test_aaa.py:具体代码如下

    # -*- coding:utf-8 -*-
    import unittest
    from selenium import webdriver
    import time
    
    #QingQing类的名字任意命名,但命名()里的unittest.TestCase就是去继承这个类,类的作用就是可以使runner.run识别
    class QingQing(unittest.TestCase):
        #unittest.TestCase类定义的setUpClass和tearDownClass方法前一定要加@classmethod,
        #setUpClass在这个类里面是第一个执行的方法
        #tearDownClass在这个类里面是最后一个执行的方法
        #中间的执行顺序是通过字符的大小进行顺序执行,命名必须test_开头
    
        #打开浏览器,获取配置
        @classmethod
        def setUpClass(self):
            # 实例化ChromeOptions
            options = webdriver.ChromeOptions()
            # 关闭浏览器提示信息
            options.add_argument('disable-infobars')
            # 浏览器全屏
            options.add_argument('start-fullscreen')
            driverpath = r'D:angelangelautolittlebee1driverchromedriver.exe'
            #driver驱动获取后可以被其他方法调用
            self.driver = webdriver.Chrome(driverpath, options=options)
    
        def test_01_search_baidu(self):
            # 访问百度首页
            self.driver.get(r"http://www.baidu.com")
            # 百度输入框输入
            self.driver.find_element_by_id("kw").send_keys("懒勺")
            # 点百度一下
            self.driver.find_element_by_id("su").click()
            #等待时间只是为了让你可以看到目前效果,可以省略
            time.sleep(2)
    
    
        #执行商品收费功能
        def test_02_search_qq_news(self):
            # 访问qq首页
            self.driver.get(r"http://www.qq.com")
            # 点新闻链接
            self.driver.find_element_by_xpath("//a[text()='新闻']").click()
            # 等待时间只是为了让你可以看到目前效果,可以省略
            time.sleep(3)
    
        #退出浏览器
        @classmethod
        def tearDownClass(self):
            self.driver.quit()
    
    if __name__ ==  "__main__":
        unittest.main()

    HTMLTestRunnerForPy3.py:具体代码如下(第三方工具类,直接使用即可)

      1 """
      2 A TestRunner for use with the Python unit testing framework. It
      3 generates a HTML report to show the result at a glance.
      4 
      5 The simplest way to use this is to invoke its main method. E.g.
      6 
      7     import unittest
      8     import HTMLTestRunner
      9 
     10     ... define your tests ...
     11 
     12     if __name__ == '__main__':
     13         HTMLTestRunner.main()
     14 
     15 
     16 For more customization options, instantiates a HTMLTestRunner object.
     17 HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
     18 
     19     # output to a file
     20     fp = file('my_report.html', 'wb')
     21     runner = HTMLTestRunner.HTMLTestRunner(
     22                 stream=fp,
     23                 title='My unit test',
     24                 description='This demonstrates the report output by HTMLTestRunner.'
     25                 )
     26 
     27     # Use an external stylesheet.
     28     # See the Template_mixin class for more customizable options
     29     runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'
     30 
     31     # run the test
     32     runner.run(my_test_suite)
     33 
     34 
     35 ------------------------------------------------------------------------
     36 Copyright (c) 2004-2007, Wai Yip Tung
     37 All rights reserved.
     38 
     39 Redistribution and use in source and binary forms, with or without
     40 modification, are permitted provided that the following conditions are
     41 met:
     42 
     43 * Redistributions of source code must retain the above copyright notice,
     44   this list of conditions and the following disclaimer.
     45 * Redistributions in binary form must reproduce the above copyright
     46   notice, this list of conditions and the following disclaimer in the
     47   documentation and/or other materials provided with the distribution.
     48 * Neither the name Wai Yip Tung nor the names of its contributors may be
     49   used to endorse or promote products derived from this software without
     50   specific prior written permission.
     51 
     52 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     53 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     54 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     55 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
     56 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     57 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     58 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     59 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     60 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     61 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     62 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     63 """
     64 
     65 # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
     66 
     67 __author__ = "Wai Yip Tung"
     68 __version__ = "0.8.2"
     69 
     70 
     71 """
     72 Change History
     73 
     74 Version 0.8.2
     75 * Show output inline instead of popup window (Viorel Lupu).
     76 
     77 Version in 0.8.1
     78 * Validated XHTML (Wolfgang Borgert).
     79 * Added description of test classes and test cases.
     80 
     81 Version in 0.8.0
     82 * Define Template_mixin class for customization.
     83 * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
     84 
     85 Version in 0.7.1
     86 * Back port to Python 2.3 (Frank Horowitz).
     87 * Fix missing scroll bars in detail logs (Podi).
     88 """
     89 
     90 # TODO: color stderr
     91 # TODO: simplify javascript using ,ore than 1 class in the class attribute?
     92 
     93 import datetime
     94 import io
     95 import sys
     96 import time
     97 import unittest
     98 from xml.sax import saxutils
     99 
    100 
    101 # ------------------------------------------------------------------------
    102 # The redirectors below are used to capture output during testing. Output
    103 # sent to sys.stdout and sys.stderr are automatically captured. However
    104 # in some cases sys.stdout is already cached before HTMLTestRunner is
    105 # invoked (e.g. calling logging.basicConfig). In order to capture those
    106 # output, use the redirectors for the cached stream.
    107 #
    108 # e.g.
    109 #   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
    110 #   >>>
    111 
    112 class OutputRedirector(object):
    113     """ Wrapper to redirect stdout or stderr """
    114     def __init__(self, fp):
    115         self.fp = fp
    116 
    117     def write(self, s):
    118         # self.fp.write(s)
    119         self.fp.write(bytes(s, 'UTF-8'))
    120 
    121     def writelines(self, lines):
    122         self.fp.writelines(lines)
    123 
    124     def flush(self):
    125         self.fp.flush()
    126 
    127 stdout_redirector = OutputRedirector(sys.stdout)
    128 stderr_redirector = OutputRedirector(sys.stderr)
    129 
    130 
    131 
    132 # ----------------------------------------------------------------------
    133 # Template
    134 
    135 class Template_mixin(object):
    136     """
    137     Define a HTML template for report customerization and generation.
    138 
    139     Overall structure of an HTML report
    140 
    141     HTML
    142     +------------------------+
    143     |<html>                  |
    144     |  <head>                |
    145     |                        |
    146     |   STYLESHEET           |
    147     |   +----------------+   |
    148     |   |                |   |
    149     |   +----------------+   |
    150     |                        |
    151     |  </head>               |
    152     |                        |
    153     |  <body>                |
    154     |                        |
    155     |   HEADING              |
    156     |   +----------------+   |
    157     |   |                |   |
    158     |   +----------------+   |
    159     |                        |
    160     |   REPORT               |
    161     |   +----------------+   |
    162     |   |                |   |
    163     |   +----------------+   |
    164     |                        |
    165     |   ENDING               |
    166     |   +----------------+   |
    167     |   |                |   |
    168     |   +----------------+   |
    169     |                        |
    170     |  </body>               |
    171     |</html>                 |
    172     +------------------------+
    173     """
    174 
    175     STATUS = {
    176     0: 'pass',
    177     1: 'fail',
    178     2: 'error',
    179     }
    180 
    181     DEFAULT_TITLE = 'Unit Test Report'
    182     DEFAULT_DESCRIPTION = ''
    183 
    184     # ------------------------------------------------------------------------
    185     # HTML Template
    186 
    187     HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
    188 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    189 <html xmlns="http://www.w3.org/1999/xhtml">
    190 <head>
    191     <title>%(title)s</title>
    192     <meta name="generator" content="%(generator)s"/>
    193     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    194     %(stylesheet)s
    195 </head>
    196 <body>
    197 <script language="javascript" type="text/javascript"><!--
    198 output_list = Array();
    199 
    200 /* level - 0:Summary; 1:Failed; 2:All */
    201 function showCase(level) {
    202     trs = document.getElementsByTagName("tr");
    203     for (var i = 0; i < trs.length; i++) {
    204         tr = trs[i];
    205         id = tr.id;
    206         if (id.substr(0,2) == 'ft') {
    207             if (level < 1) {
    208                 tr.className = 'hiddenRow';
    209             }
    210             else {
    211                 tr.className = '';
    212             }
    213         }
    214         if (id.substr(0,2) == 'pt') {
    215             if (level > 1) {
    216                 tr.className = '';
    217             }
    218             else {
    219                 tr.className = 'hiddenRow';
    220             }
    221         }
    222     }
    223 }
    224 
    225 
    226 function showClassDetail(cid, count) {
    227     var id_list = Array(count);
    228     var toHide = 1;
    229     for (var i = 0; i < count; i++) {
    230         tid0 = 't' + cid.substr(1) + '.' + (i+1);
    231         tid = 'f' + tid0;
    232         tr = document.getElementById(tid);
    233         if (!tr) {
    234             tid = 'p' + tid0;
    235             tr = document.getElementById(tid);
    236         }
    237         id_list[i] = tid;
    238         if (tr.className) {
    239             toHide = 0;
    240         }
    241     }
    242     for (var i = 0; i < count; i++) {
    243         tid = id_list[i];
    244         if (toHide) {
    245             document.getElementById('div_'+tid).style.display = 'none'
    246             document.getElementById(tid).className = 'hiddenRow';
    247         }
    248         else {
    249             document.getElementById(tid).className = '';
    250         }
    251     }
    252 }
    253 
    254 
    255 function showTestDetail(div_id){
    256     var details_div = document.getElementById(div_id)
    257     var displayState = details_div.style.display
    258     // alert(displayState)
    259     if (displayState != 'block' ) {
    260         displayState = 'block'
    261         details_div.style.display = 'block'
    262     }
    263     else {
    264         details_div.style.display = 'none'
    265     }
    266 }
    267 
    268 
    269 function html_escape(s) {
    270     s = s.replace(/&/g,'&amp;');
    271     s = s.replace(/</g,'&lt;');
    272     s = s.replace(/>/g,'&gt;');
    273     return s;
    274 }
    275 
    276 /* obsoleted by detail in <div>
    277 function showOutput(id, name) {
    278     var w = window.open("", //url
    279                     name,
    280                     "resizable,scrollbars,status,width=800,height=450");
    281     d = w.document;
    282     d.write("<pre>");
    283     d.write(html_escape(output_list[id]));
    284     d.write("
    ");
    285     d.write("<a href='javascript:window.close()'>close</a>
    ");
    286     d.write("</pre>
    ");
    287     d.close();
    288 }
    289 */
    290 --></script>
    291 
    292 %(heading)s
    293 %(report)s
    294 %(ending)s
    295 
    296 </body>
    297 </html>
    298 """
    299     # variables: (title, generator, stylesheet, heading, report, ending)
    300 
    301 
    302     # ------------------------------------------------------------------------
    303     # Stylesheet
    304     #
    305     # alternatively use a <link> for external style sheet, e.g.
    306     #   <link rel="stylesheet" href="$url" type="text/css">
    307 
    308     STYLESHEET_TMPL = """
    309 <style type="text/css" media="screen">
    310 body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
    311 table       { font-size: 100%; }
    312 pre         { }
    313 
    314 /* -- heading ---------------------------------------------------------------------- */
    315 h1 {
    316     font-size: 16pt;
    317     color: gray;
    318 }
    319 .heading {
    320     margin-top: 0ex;
    321     margin-bottom: 1ex;
    322 }
    323 
    324 .heading .attribute {
    325     margin-top: 1ex;
    326     margin-bottom: 0;
    327 }
    328 
    329 .heading .description {
    330     margin-top: 4ex;
    331     margin-bottom: 6ex;
    332 }
    333 
    334 /* -- css div popup ------------------------------------------------------------------------ */
    335 a.popup_link {
    336 }
    337 
    338 a.popup_link:hover {
    339     color: red;
    340 }
    341 
    342 .popup_window {
    343     display: none;
    344     position: relative;
    345     left: 0px;
    346     top: 0px;
    347     /*border: solid #627173 1px; */
    348     padding: 10px;
    349     background-color: #E6E6D6;
    350     font-family: "Lucida Console", "Courier New", Courier, monospace;
    351     text-align: left;
    352     font-size: 8pt;
    353      500px;
    354 }
    355 
    356 }
    357 /* -- report ------------------------------------------------------------------------ */
    358 #show_detail_line {
    359     margin-top: 3ex;
    360     margin-bottom: 1ex;
    361 }
    362 #result_table {
    363      80%;
    364     border-collapse: collapse;
    365     border: 1px solid #777;
    366 }
    367 #header_row {
    368     font-weight: bold;
    369     color: white;
    370     background-color: #777;
    371 }
    372 #result_table td {
    373     border: 1px solid #777;
    374     padding: 2px;
    375 }
    376 #total_row  { font-weight: bold; }
    377 .passClass  { background-color: #6c6; }
    378 .failClass  { background-color: #c60; }
    379 .errorClass { background-color: #c00; }
    380 .passCase   { color: #6c6; }
    381 .failCase   { color: #c60; font-weight: bold; }
    382 .errorCase  { color: #c00; font-weight: bold; }
    383 .hiddenRow  { display: none; }
    384 .testcase   { margin-left: 2em; }
    385 
    386 
    387 /* -- ending ---------------------------------------------------------------------- */
    388 #ending {
    389 }
    390 
    391 </style>
    392 """
    393 
    394 
    395 
    396     # ------------------------------------------------------------------------
    397     # Heading
    398     #
    399 
    400     HEADING_TMPL = """<div class='heading'>
    401 <h1>%(title)s</h1>
    402 %(parameters)s
    403 <p class='description'>%(description)s</p>
    404 </div>
    405 
    406 """ # variables: (title, parameters, description)
    407 
    408     HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
    409 """ # variables: (name, value)
    410 
    411 
    412 
    413     # ------------------------------------------------------------------------
    414     # Report
    415     #
    416 
    417     REPORT_TMPL = """
    418 <p id='show_detail_line'>Show
    419 <a href='javascript:showCase(0)'>Summary</a>
    420 <a href='javascript:showCase(1)'>Failed</a>
    421 <a href='javascript:showCase(2)'>All</a>
    422 </p>
    423 <table id='result_table'>
    424 <colgroup>
    425 <col align='left' />
    426 <col align='right' />
    427 <col align='right' />
    428 <col align='right' />
    429 <col align='right' />
    430 <col align='right' />
    431 </colgroup>
    432 <tr id='header_row'>
    433     <td>Test Group/Test case</td>
    434     <td>Count</td>
    435     <td>Pass</td>
    436     <td>Fail</td>
    437     <td>Error</td>
    438     <td>View</td>
    439 </tr>
    440 %(test_list)s
    441 <tr id='total_row'>
    442     <td>Total</td>
    443     <td>%(count)s</td>
    444     <td>%(Pass)s</td>
    445     <td>%(fail)s</td>
    446     <td>%(error)s</td>
    447     <td>&nbsp;</td>
    448 </tr>
    449 </table>
    450 """ # variables: (test_list, count, Pass, fail, error)
    451 
    452     REPORT_CLASS_TMPL = r"""
    453 <tr class='%(style)s'>
    454     <td>%(desc)s</td>
    455     <td>%(count)s</td>
    456     <td>%(Pass)s</td>
    457     <td>%(fail)s</td>
    458     <td>%(error)s</td>
    459     <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
    460 </tr>
    461 """ # variables: (style, desc, count, Pass, fail, error, cid)
    462 
    463 
    464     REPORT_TEST_WITH_OUTPUT_TMPL = r"""
    465 <tr id='%(tid)s' class='%(Class)s'>
    466     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    467     <td colspan='5' align='center'>
    468 
    469     <!--css div popup start-->
    470     <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
    471         %(status)s</a>
    472 
    473     <div id='div_%(tid)s' class="popup_window">
    474         <div style='text-align: right; color:red;cursor:pointer'>
    475         <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
    476            [x]</a>
    477         </div>
    478         <pre>
    479         %(script)s
    480         </pre>
    481     </div>
    482     <!--css div popup end-->
    483 
    484     </td>
    485 </tr>
    486 """ # variables: (tid, Class, style, desc, status)
    487 
    488 
    489     REPORT_TEST_NO_OUTPUT_TMPL = r"""
    490 <tr id='%(tid)s' class='%(Class)s'>
    491     <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
    492     <td colspan='5' align='center'>%(status)s</td>
    493 </tr>
    494 """ # variables: (tid, Class, style, desc, status)
    495 
    496 
    497     REPORT_TEST_OUTPUT_TMPL = r"""
    498 %(id)s: %(output)s
    499 """ # variables: (id, output)
    500 
    501 
    502 
    503     # ------------------------------------------------------------------------
    504     # ENDING
    505     #
    506 
    507     ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""
    508 
    509 # -------------------- The end of the Template class -------------------
    510 
    511 
    512 TestResult = unittest.TestResult
    513 
    514 class _TestResult(TestResult):
    515     # note: _TestResult is a pure representation of results.
    516     # It lacks the output and reporting ability compares to unittest._TextTestResult.
    517 
    518     def __init__(self, verbosity=1):
    519         TestResult.__init__(self)
    520         self.stdout0 = None
    521         self.stderr0 = None
    522         self.success_count = 0
    523         self.failure_count = 0
    524         self.error_count = 0
    525         self.verbosity = verbosity
    526 
    527         # result is a list of result in 4 tuple
    528         # (
    529         #   result code (0: success; 1: fail; 2: error),
    530         #   TestCase object,
    531         #   Test output (byte string),
    532         #   stack trace,
    533         # )
    534         self.result = []
    535 
    536 
    537     def startTest(self, test):
    538         TestResult.startTest(self, test)
    539         # just one buffer for both stdout and stderr
    540         self.outputBuffer = io.StringIO()
    541         stdout_redirector.fp = self.outputBuffer
    542         stderr_redirector.fp = self.outputBuffer
    543         self.stdout0 = sys.stdout
    544         self.stderr0 = sys.stderr
    545         sys.stdout = stdout_redirector
    546         sys.stderr = stderr_redirector
    547 
    548 
    549     def complete_output(self):
    550         """
    551         Disconnect output redirection and return buffer.
    552         Safe to call multiple times.
    553         """
    554         if self.stdout0:
    555             sys.stdout = self.stdout0
    556             sys.stderr = self.stderr0
    557             self.stdout0 = None
    558             self.stderr0 = None
    559         return self.outputBuffer.getvalue()
    560 
    561 
    562     def stopTest(self, test):
    563         # Usually one of addSuccess, addError or addFailure would have been called.
    564         # But there are some path in unittest that would bypass this.
    565         # We must disconnect stdout in stopTest(), which is guaranteed to be called.
    566         self.complete_output()
    567 
    568 
    569     def addSuccess(self, test):
    570         self.success_count += 1
    571         TestResult.addSuccess(self, test)
    572         output = self.complete_output()
    573         self.result.append((0, test, output, ''))
    574         if self.verbosity > 1:
    575             sys.stderr.write('ok ')
    576             sys.stderr.write(str(test))
    577             sys.stderr.write('
    ')
    578         else:
    579             sys.stderr.write('.')
    580 
    581     def addError(self, test, err):
    582         self.error_count += 1
    583         TestResult.addError(self, test, err)
    584         _, _exc_str = self.errors[-1]
    585         output = self.complete_output()
    586         self.result.append((2, test, output, _exc_str))
    587         if self.verbosity > 1:
    588             sys.stderr.write('E  ')
    589             sys.stderr.write(str(test))
    590             sys.stderr.write('
    ')
    591         else:
    592             sys.stderr.write('E')
    593 
    594     def addFailure(self, test, err):
    595         self.failure_count += 1
    596         TestResult.addFailure(self, test, err)
    597         _, _exc_str = self.failures[-1]
    598         output = self.complete_output()
    599         self.result.append((1, test, output, _exc_str))
    600         if self.verbosity > 1:
    601             sys.stderr.write('F  ')
    602             sys.stderr.write(str(test))
    603             sys.stderr.write('
    ')
    604         else:
    605             sys.stderr.write('F')
    606 
    607 
    608 class HTMLTestRunner(Template_mixin):
    609     """
    610     """
    611     def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
    612         self.stream = stream
    613         self.verbosity = verbosity
    614         if title is None:
    615             self.title = self.DEFAULT_TITLE
    616         else:
    617             self.title = title
    618         if description is None:
    619             self.description = self.DEFAULT_DESCRIPTION
    620         else:
    621             self.description = description
    622 
    623         self.startTime = datetime.datetime.now()
    624 
    625 
    626     def run(self, test):
    627         "Run the given test case or test suite."
    628         result = _TestResult(self.verbosity)
    629         test(result)
    630         self.stopTime = datetime.datetime.now()
    631         self.generateReport(test, result)
    632         # print >>sys.stderr, '
    Time Elapsed: %s' % (self.stopTime-self.startTime)
    633         print('
    Time Elapsed: %s' % (self.stopTime - self.startTime), file=sys.stderr)
    634         return result
    635 
    636 
    637     def sortResult(self, result_list):
    638         # unittest does not seems to run in any particular order.
    639         # Here at least we want to group them together by class.
    640         rmap = {}
    641         classes = []
    642         for n,t,o,e in result_list:
    643             cls = t.__class__
    644             if not cls in rmap:
    645                 rmap[cls] = []
    646                 classes.append(cls)
    647             rmap[cls].append((n,t,o,e))
    648         r = [(cls, rmap[cls]) for cls in classes]
    649         return r
    650 
    651 
    652     def getReportAttributes(self, result):
    653         """
    654         Return report attributes as a list of (name, value).
    655         Override this to add custom attributes.
    656         """
    657         startTime = str(self.startTime)[:19]
    658         duration = str(self.stopTime - self.startTime)
    659         status = []
    660         if result.success_count: status.append('Pass %s'    % result.success_count)
    661         if result.failure_count: status.append('Failure %s' % result.failure_count)
    662         if result.error_count:   status.append('Error %s'   % result.error_count  )
    663         if status:
    664             status = ' '.join(status)
    665         else:
    666             status = 'none'
    667         return [
    668             ('Start Time', startTime),
    669             ('Duration', duration),
    670             ('Status', status),
    671         ]
    672 
    673 
    674     def generateReport(self, test, result):
    675         report_attrs = self.getReportAttributes(result)
    676         generator = 'HTMLTestRunner %s' % __version__
    677         stylesheet = self._generate_stylesheet()
    678         heading = self._generate_heading(report_attrs)
    679         report = self._generate_report(result)
    680         ending = self._generate_ending()
    681         output = self.HTML_TMPL % dict(
    682             title = saxutils.escape(self.title),
    683             generator = generator,
    684             stylesheet = stylesheet,
    685             heading = heading,
    686             report = report,
    687             ending = ending,
    688         )
    689         self.stream.write(output.encode('utf8'))
    690 
    691 
    692     def _generate_stylesheet(self):
    693         return self.STYLESHEET_TMPL
    694 
    695 
    696     def _generate_heading(self, report_attrs):
    697         a_lines = []
    698         for name, value in report_attrs:
    699             line = self.HEADING_ATTRIBUTE_TMPL % dict(
    700                     name = saxutils.escape(name),
    701                     value = saxutils.escape(value),
    702                 )
    703             a_lines.append(line)
    704         heading = self.HEADING_TMPL % dict(
    705             title = saxutils.escape(self.title),
    706             parameters = ''.join(a_lines),
    707             description = saxutils.escape(self.description),
    708         )
    709         return heading
    710 
    711 
    712     def _generate_report(self, result):
    713         rows = []
    714         sortedResult = self.sortResult(result.result)
    715         for cid, (cls, cls_results) in enumerate(sortedResult):
    716             # subtotal for a class
    717             np = nf = ne = 0
    718             for n,t,o,e in cls_results:
    719                 if n == 0: np += 1
    720                 elif n == 1: nf += 1
    721                 else: ne += 1
    722 
    723             # format class description
    724             if cls.__module__ == "__main__":
    725                 name = cls.__name__
    726             else:
    727                 name = "%s.%s" % (cls.__module__, cls.__name__)
    728             doc = cls.__doc__ and cls.__doc__.split("
    ")[0] or ""
    729             desc = doc and '%s: %s' % (name, doc) or name
    730 
    731             row = self.REPORT_CLASS_TMPL % dict(
    732                 style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
    733                 desc = desc,
    734                 count = np+nf+ne,
    735                 Pass = np,
    736                 fail = nf,
    737                 error = ne,
    738                 cid = 'c%s' % (cid+1),
    739             )
    740             rows.append(row)
    741 
    742             for tid, (n,t,o,e) in enumerate(cls_results):
    743                 self._generate_report_test(rows, cid, tid, n, t, o, e)
    744 
    745         report = self.REPORT_TMPL % dict(
    746             test_list = ''.join(rows),
    747             count = str(result.success_count+result.failure_count+result.error_count),
    748             Pass = str(result.success_count),
    749             fail = str(result.failure_count),
    750             error = str(result.error_count),
    751         )
    752         return report
    753 
    754 
    755     def _generate_report_test(self, rows, cid, tid, n, t, o, e):
    756         # e.g. 'pt1.1', 'ft1.1', etc
    757         has_output = bool(o or e)
    758         tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
    759         name = t.id().split('.')[-1]
    760         doc = t.shortDescription() or ""
    761         desc = doc and ('%s: %s' % (name, doc)) or name
    762         tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
    763 
    764         # o and e should be byte string because they are collected from stdout and stderr?
    765         if isinstance(o,str):
    766             # TODO: some problem with 'string_escape': it escape 
     and mess up formating
    767             # uo = unicode(o.encode('string_escape'))
    768             uo = o
    769         else:
    770             uo =  o.decode('utf-8')
    771         if isinstance(e,str):
    772             # TODO: some problem with 'string_escape': it escape 
     and mess up formating
    773             # ue = unicode(e.encode('string_escape'))
    774             ue = e
    775         else:
    776             ue = e.decode('utf-8')
    777 
    778         script = self.REPORT_TEST_OUTPUT_TMPL % dict(
    779             id = tid,
    780             output = saxutils.escape(uo+ue),
    781         )
    782 
    783         row = tmpl % dict(
    784             tid = tid,
    785             Class = (n == 0 and 'hiddenRow' or 'none'),
    786             style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
    787             desc = desc,
    788             script = script,
    789             status = self.STATUS[n],
    790         )
    791         rows.append(row)
    792         if not has_output:
    793             return
    794 
    795     def _generate_ending(self):
    796         return self.ENDING_TMPL
    797 
    798 
    799 ##############################################################################
    800 # Facilities for running tests from the command line
    801 ##############################################################################
    802 
    803 # Note: Reuse unittest.TestProgram to launch test. In the future we may
    804 # build our own launcher to support more specific command line
    805 # parameters like test title, CSS, etc.
    806 class TestProgram(unittest.TestProgram):
    807     """
    808     A variation of the unittest.TestProgram. Please refer to the base
    809     class for command line parameters.
    810     """
    811     def runTests(self):
    812         # Pick HTMLTestRunner as the default test runner.
    813         # base class's testRunner parameter is not useful because it means
    814         # we have to instantiate HTMLTestRunner before we know self.verbosity.
    815         if self.testRunner is None:
    816             self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
    817         unittest.TestProgram.runTests(self)
    818 
    819 main = TestProgram
    820 
    821 ##############################################################################
    822 # Executing this module from the command line
    823 ##############################################################################
    824 
    825 if __name__ == "__main__":
    826     main(module=None)
    View Code

    生成报告效果

     test_aaa.py:具体代码如下

  • 相关阅读:
    Android studio开发找不到HttpClient问题
    Android studio开发找不到HttpClient问题
    互联网应用之传递HTTP参数
    互联网应用之传递HTTP参数
    计算机组成原理
    计算机组成原理
    【NYOJ】[40]公约数和公倍数
    【NYOJ】[40]公约数和公倍数
    【NYOJ】[39]水仙花数
    【NYOJ】[39]水仙花数
  • 原文地址:https://www.cnblogs.com/heng-xin/p/14084056.html
Copyright © 2011-2022 走看看