• Appium python自动化测试系列之使用HTMLTestRunner生成测试报告(十三)


    ​13.1 测试报告概述

    13.1.1 测试报告的定义

    在前面章节我们已经讲了自动化基础的很多东西,如果说掌握了,而且自己动手去练习了,我相信在一些初级的面试中是没任何问题的,今天我们接触的应该算是一个比较新的东西,也算是开启另一层知识的大门。

    在手工测试过程中如果发现了bug我们需要提交测试报告,自动化中虽然当程序出错的时候我们不可能让程序自动去提交bug(其实也可以,只是需要自己去开发模块,思路:当程序监听到错误的时候就触发一个提交bug的程序),但是为了体现工作的价值当自动化跑完之后是不是就需要生成一份测试报告呢,让我们知道跑了哪些case,哪些通过哪些没通过,通过率是多少。所以在这个情况下我们就有了测试报告

    13.1.2 控制台消息和HTML报告的区别

    其实很多人不明白为什么有控制台的消息不看,非得看html的报告,那不是抽风么?其实我刚开始做的时候也是一样,觉得控制台的消息一经提示很明确了,而且排错什么的也很好,结合TestNG的看就更方便,其实很长一段时间我确实是这么做的,但是后来有一次领导让我分享一下,我按照平时的工作流程做了,后来的结果是这个需要提高,说:你这个还不能完全叫做自动化。其实当时我想为什么不能呢?后来通过各方面的请教以及收集资料才知道,自己那个真的不叫。就这个很好的例子:每次跑脚本还需要打开IDE然后运行去看报告,不low吗?如果说你的自动化是自动运行,或者执行一个bat,或者是打开一个网址点一个按钮,然后收到的就是一份完整的HTML报告,这样是不是顿时感觉档次上去了呢?可能有人会疑惑,说你报告里面会有运行错误时的一些信息吗?答案肯定是有的。哪个case错误、哪里错误、错误的截图、错误的日志统统的有啦,还有case的通过率,难道这样的报告不比控制台好么?

    13.2 如何拥有HTML测试报告

    13.2.1 HTMLTestRunner

    HTMLTestRunner是python标准库中单元测试模块的一个扩展,在使用python的情况下我们基本都是使用,还有一个事情需要说的是,他是一个扩展,那么就需要自己去安装,当然如果你在python的命令模式下直接引入“import HTMLTestRunner”一下,如果你那个不报错那么代表你有这个环境,不需要另外配置,如果没配置接着下看。

    13.2.2 HTMLTestRunner 环境配置

    首先我们来看一下我电脑中的配置图片:

    从上面的图片我们可以在出在“/Library/Python/2.7/site-packages” 这个目录下面就是我们python的一些扩展包,然后在这个里面我有两个文件,“HTMLTestRunner.py”,“test_HTMLTestRunner.py”,这两个文件就是我们生成html测试报告的时候需要的东西,当然实际上只需要第一个,这个就当作官网的教程吧。大家也可以去下载HTMLTestRunner.py这个文件然后去放到这个目录下面,如果没找到也可以对下面的代码进行复制:

    """
    A TestRunner for use with the Python unit testing framework. It
    generates a HTML report to show the result at a glance.
       
    The simplest way to use this is to invoke its main method. E.g.
       
        import unittest
        import HTMLTestRunner
       
        ... define your tests ...
       
        if __name__ == '__main__':
            HTMLTestRunner.main()
       
       
    For more customization options, instantiates a HTMLTestRunner object.
    HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
       
        # output to a file
        fp = file('my_report.html', 'wb')
        runner = HTMLTestRunner.HTMLTestRunner(
                    stream=fp,
                    title='My unit test',
                    description='This demonstrates the report output by HTMLTestRunner.'
                    )
       
        # Use an external stylesheet.
        # See the Template_mixin class for more customizable options
        runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'
       
        # run the test
        runner.run(my_test_suite)
       
       
    ------------------------------------------------------------------------
    Copyright (c) 2004-2007, Wai Yip Tung
    All rights reserved.
       
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:
       
    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name Wai Yip Tung nor the names of its contributors may be
      used to endorse or promote products derived from this software without
      specific prior written permission.
       
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    """
       
    # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
       
    __author__ = "Wai Yip Tung"
    __version__ = "0.8.2"
       
       
    """
    Change History
       
    Version 0.8.2
    * Show output inline instead of popup window (Viorel Lupu).
       
    Version in 0.8.1
    * Validated XHTML (Wolfgang Borgert).
    * Added description of test classes and test cases.
       
    Version in 0.8.0
    * Define Template_mixin class for customization.
    * Workaround a IE 6 bug that it does not treat <script> block as CDATA.
       
    Version in 0.7.1
    * Back port to Python 2.3 (Frank Horowitz).
    * Fix missing scroll bars in detail log (Podi).
    """
       
    # TODO: color stderr
    # TODO: simplify javascript using ,ore than 1 class in the class attribute?
       
    import datetime
    import StringIO
    import sys
    import time
    import unittest
    from xml.sax import saxutils
       
       
    # ------------------------------------------------------------------------
    # The redirectors below are used to capture output during testing. Output
    # sent to sys.stdout and sys.stderr are automatically captured. However
    # in some cases sys.stdout is already cached before HTMLTestRunner is
    # invoked (e.g. calling logging.basicConfig). In order to capture those
    # output, use the redirectors for the cached stream.
    #
    # e.g.
    #   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
    #   >>>
       
    class OutputRedirector(object):
        """ Wrapper to redirect stdout or stderr """
        def __init__(self, fp):
            self.fp = fp
       
        def write(self, s):
            self.fp.write(s)
       
        def writelines(self, lines):
            self.fp.writelines(lines)
       
        def flush(self):
            self.fp.flush()
       
    stdout_redirector = OutputRedirector(sys.stdout)
    stderr_redirector = OutputRedirector(sys.stderr)
       
       
       
    # ----------------------------------------------------------------------
    # Template
       
    class Template_mixin(object):
        """
        Define a HTML template for report customerization and generation.
       
        Overall structure of an HTML report
       
        HTML
        +------------------------+
        |<html>                  |
        |  <head>                |
        |                        |
        |   STYLESHEET           |
        |   +----------------+   |
        |   |                |   |
        |   +----------------+   |
        |                        |
        |  </head>               |
        |                        |
        |  <body>                |
        |                        |
        |   HEADING              |
        |   +----------------+   |
        |   |                |   |
        |   +----------------+   |
        |                        |
        |   REPORT               |
        |   +----------------+   |
        |   |                |   |
        |   +----------------+   |
        |                        |
        |   ENDING               |
        |   +----------------+   |
        |   |                |   |
        |   +----------------+   |
        |                        |
        |  </body>               |
        |</html>                 |
        +------------------------+
        """
       
        STATUS = {
        0: 'pass',
        1: 'fail',
        2: 'error',
        }
       
        DEFAULT_TITLE = 'Unit Test Report'
        DEFAULT_DESCRIPTION = ''
       
        # ------------------------------------------------------------------------
        # HTML Template
       
        HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>%(title)s</title>
        <meta name="generator" content="%(generator)s"/>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        %(stylesheet)s
    </head>
    <body>
    <script language="javascript" type="text/javascript"><!--
    output_list = Array();
       
    /* level - 0:Summary; 1:Failed; 2:All */
    function showCase(level) {
        trs = document.getElementsByTagName("tr");
        for (var i = 0; i < trs.length; i++) {
            tr = trs[i];
            id = tr.id;
            if (id.substr(0,2) == 'ft') {
                if (level < 1) {
                    tr.className = 'hiddenRow';
                }
                else {
                    tr.className = '';
                }
            }
            if (id.substr(0,2) == 'pt') {
                if (level > 1) {
                    tr.className = '';
                }
                else {
                    tr.className = 'hiddenRow';
                }
            }
        }
    }
       
       
    function showClassDetail(cid, count) {
        var id_list = Array(count);
        var toHide = 1;
        for (var i = 0; i < count; i++) {
            tid0 = 't' + cid.substr(1) + '.' + (i+1);
            tid = 'f' + tid0;
            tr = document.getElementById(tid);
            if (!tr) {
                tid = 'p' + tid0;
                tr = document.getElementById(tid);
            }
            id_list[i] = tid;
            if (tr.className) {
                toHide = 0;
            }
        }
        for (var i = 0; i < count; i++) {
            tid = id_list[i];
            if (toHide) {
                document.getElementById('div_'+tid).style.display = 'none'
                document.getElementById(tid).className = 'hiddenRow';
            }
            else {
                document.getElementById(tid).className = '';
            }
        }
    }
       
       
    function showTestDetail(div_id){
        var details_div = document.getElementById(div_id)
        var displayState = details_div.style.display
        // alert(displayState)
        if (displayState != 'block' ) {
            displayState = 'block'
            details_div.style.display = 'block'
        }
        else {
            details_div.style.display = 'none'
        }
    }
       
       
    function html_escape(s) {
        s = s.replace(/&/g,'&');
        s = s.replace(/</g,'<');
        s = s.replace(/>/g,'>');
        return s;
    }
       
    /* obsoleted by detail in <div>
    function showOutput(id, name) {
        var w = window.open("", //url
                        name,
                        "resizable,scrollbars,status,width=800,height=450");
        d = w.document;
        d.write("<pre>");
        d.write(html_escape(output_list[id]));
        d.write("
    ");
        d.write("<a href='javascript:window.close()'>close</a>
    ");
        d.write("</pre>
    ");
        d.close();
    }
    */
    --></script>
       
    %(heading)s
    %(report)s
    %(ending)s
       
    </body>
    </html>
    """
        # variables: (title, generator, stylesheet, heading, report, ending)
       
       
        # ------------------------------------------------------------------------
        # Stylesheet
        #
        # alternatively use a <link> for external style sheet, e.g.
        #   <link rel="stylesheet" href="$url" type="text/css">
       
        STYLESHEET_TMPL = """
    <style type="text/css" media="screen">
    body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
    table       { font-size: 100%; }
    pre         { }
       
    /* -- heading ---------------------------------------------------------------------- */
    h1 {
      font-size: 16pt;
      color: gray;
    }
    .heading {
        margin-top: 0ex;
        margin-bottom: 1ex;
    }
       
    .heading .attribute {
        margin-top: 1ex;
        margin-bottom: 0;
    }
       
    .heading .description {
        margin-top: 4ex;
        margin-bottom: 6ex;
    }
       
    /* -- css div popup ------------------------------------------------------------------------ */
    a.popup_link {
    }
       
    a.popup_link:hover {
        color: red;
    }
       
    .popup_window {
        display: none;
        position: relative;
        left: 0px;
        top: 0px;
        /*border: solid #627173 1px; */
        padding: 10px;
        background-color: #E6E6D6;
        font-family: "Lucida Console", "Courier New", Courier, monospace;
        text-align: left;
        font-size: 8pt;
         500px;
    }
       
    }
    /* -- report ------------------------------------------------------------------------ */
    #show_detail_line {
        margin-top: 3ex;
        margin-bottom: 1ex;
    }
    #result_table {
         80%;
        border-collapse: collapse;
        border: 1px solid #777;
    }
    #header_row {
        font-weight: bold;
        color: white;
        background-color: #777;
    }
    #result_table td {
        border: 1px solid #777;
        padding: 2px;
    }
    #total_row  { font-weight: bold; }
    .passClass  { background-color: #6c6; }
    .failClass  { background-color: #c60; }
    .errorClass { background-color: #c00; }
    .passCase   { color: #6c6; }
    .failCase   { color: #c60; font-weight: bold; }
    .errorCase  { color: #c00; font-weight: bold; }
    .hiddenRow  { display: none; }
    .testcase   { margin-left: 2em; }
       
       
    /* -- ending ---------------------------------------------------------------------- */
    #ending {
    }
       
    </style>
    """
       
       
       
        # ------------------------------------------------------------------------
        # Heading
        #
       
        HEADING_TMPL = """<div class='heading'>
    <h1>%(title)s</h1>
    %(parameters)s
    <p class='description'>%(description)s</p>
    </div>
       
    """ # variables: (title, parameters, description)
       
        HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
    """ # variables: (name, value)
       
       
       
        # ------------------------------------------------------------------------
        # Report
        #
       
        REPORT_TMPL = """
    <p id='show_detail_line'>Show
    <a href='javascript:showCase(0)'>Summary</a>
    <a href='javascript:showCase(1)'>Failed</a>
    <a href='javascript:showCase(2)'>All</a>
    </p>
    <table id='result_table'>
    <colgroup>
    <col align='left' />
    <col align='right' />
    <col align='right' />
    <col align='right' />
    <col align='right' />
    <col align='right' />
    </colgroup>
    <tr id='header_row'>
        <td>Test Group/Test case</td>
        <td>Count</td>
        <td>Pass</td>
        <td>Fail</td>
        <td>Error</td>
        <td>View</td>
    </tr>
    %(test_list)s
    <tr id='total_row'>
        <td>Total</td>
        <td>%(count)s</td>
        <td>%(Pass)s</td>
        <td>%(fail)s</td>
        <td>%(error)s</td>
        <td> </td>
    </tr>
    </table>
    """ # variables: (test_list, count, Pass, fail, error)
       
        REPORT_CLASS_TMPL = r"""
    <tr class='%(style)s'>
        <td>%(desc)s</td>
        <td>%(count)s</td>
        <td>%(Pass)s</td>
        <td>%(fail)s</td>
        <td>%(error)s</td>
        <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
    </tr>
    """ # variables: (style, desc, count, Pass, fail, error, cid)
       
       
        REPORT_TEST_WITH_OUTPUT_TMPL = r"""
    <tr id='%(tid)s' class='%(Class)s'>
        <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
        <td colspan='5' align='center'>
       
        <!--css div popup start-->
        <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
            %(status)s</a>
       
        <div id='div_%(tid)s' class="popup_window">
            <div style='text-align: right; color:red;cursor:pointer'>
            <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
               [x]</a>
            </div>
            <pre>
            %(script)s
            </pre>
        </div>
        <!--css div popup end-->
       
        </td>
    </tr>
    """ # variables: (tid, Class, style, desc, status)
       
       
        REPORT_TEST_NO_OUTPUT_TMPL = r"""
    <tr id='%(tid)s' class='%(Class)s'>
        <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
        <td colspan='5' align='center'>%(status)s</td>
    </tr>
    """ # variables: (tid, Class, style, desc, status)
       
       
        REPORT_TEST_OUTPUT_TMPL = r"""
    %(id)s: %(output)s
    """ # variables: (id, output)
       
       
       
        # ------------------------------------------------------------------------
        # ENDING
        #
       
        ENDING_TMPL = """<div id='ending'> </div>"""
       
    # -------------------- The end of the Template class -------------------
       
       
    TestResult = unittest.TestResult
       
    class _TestResult(TestResult):
        # note: _TestResult is a pure representation of results.
        # It lacks the output and reporting ability compares to unittest._TextTestResult.
       
        def __init__(self, verbosity=1):
            TestResult.__init__(self)
            self.stdout0 = None
            self.stderr0 = None
            self.success_count = 0
            self.failure_count = 0
            self.error_count = 0
            self.verbosity = verbosity
       
            # result is a list of result in 4 tuple
            # (
            #   result code (0: success; 1: fail; 2: error),
            #   TestCase object,
            #   Test output (byte string),
            #   stack trace,
            # )
            self.result = []
       
       
        def startTest(self, test):
            TestResult.startTest(self, test)
            # just one buffer for both stdout and stderr
            self.outputBuffer = StringIO.StringIO()
            stdout_redirector.fp = self.outputBuffer
            stderr_redirector.fp = self.outputBuffer
            self.stdout0 = sys.stdout
            self.stderr0 = sys.stderr
            sys.stdout = stdout_redirector
            sys.stderr = stderr_redirector
       
       
        def complete_output(self):
            """
            Disconnect output redirection and return buffer.
            Safe to call multiple times.
            """
            if self.stdout0:
                sys.stdout = self.stdout0
                sys.stderr = self.stderr0
                self.stdout0 = None
                self.stderr0 = None
            return self.outputBuffer.getvalue()
       
       
        def stopTest(self, test):
            # Usually one of addSuccess, addError or addFailure would have been called.
            # But there are some path in unittest that would bypass this.
            # We must disconnect stdout in stopTest(), which is guaranteed to be called.
            self.complete_output()
       
       
        def addSuccess(self, test):
            self.success_count += 1
            TestResult.addSuccess(self, test)
            output = self.complete_output()
            self.result.append((0, test, output, ''))
            if self.verbosity > 1:
                sys.stderr.write('ok ')
                sys.stderr.write(str(test))
                sys.stderr.write('
    ')
            else:
                sys.stderr.write('.')
       
        def addError(self, test, err):
            self.error_count += 1
            TestResult.addError(self, test, err)
            _, _exc_str = self.errors[-1]
            output = self.complete_output()
            self.result.append((2, test, output, _exc_str))
            if self.verbosity > 1:
                sys.stderr.write('E  ')
                sys.stderr.write(str(test))
                sys.stderr.write('
    ')
            else:
                sys.stderr.write('E')
       
        def addFailure(self, test, err):
            self.failure_count += 1
            TestResult.addFailure(self, test, err)
            _, _exc_str = self.failures[-1]
            output = self.complete_output()
            self.result.append((1, test, output, _exc_str))
            if self.verbosity > 1:
                sys.stderr.write('F  ')
                sys.stderr.write(str(test))
                sys.stderr.write('
    ')
            else:
                sys.stderr.write('F')
       
       
    class HTMLTestRunner(Template_mixin):
        """
        """
        def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
            self.stream = stream
            self.verbosity = verbosity
            if title is None:
                self.title = self.DEFAULT_TITLE
            else:
                self.title = title
            if description is None:
                self.description = self.DEFAULT_DESCRIPTION
            else:
                self.description = description
       
            self.startTime = datetime.datetime.now()
       
       
        def run(self, test):
            "Run the given test case or test suite."
            result = _TestResult(self.verbosity)
            test(result)
            self.stopTime = datetime.datetime.now()
            self.generateReport(test, result)
            print >>sys.stderr, '
    Time Elapsed: %s' % (self.stopTime-self.startTime)
            return result
       
       
        def sortResult(self, result_list):
            # unittest does not seems to run in any particular order.
            # Here at least we want to group them together by class.
            rmap = {}
            classes = []
            for n,t,o,e in result_list:
                cls = t.__class__
                if not rmap.has_key(cls):
                    rmap[cls] = []
                    classes.append(cls)
                rmap[cls].append((n,t,o,e))
            r = [(cls, rmap[cls]) for cls in classes]
            return r
       
       
        def getReportAttributes(self, result):
            """
            Return report attributes as a list of (name, value).
            Override this to add custom attributes.
            """
            startTime = str(self.startTime)[:19]
            duration = str(self.stopTime - self.startTime)
            status = []
            if result.success_count: status.append('Pass %s'    % result.success_count)
            if result.failure_count: status.append('Failure %s' % result.failure_count)
            if result.error_count:   status.append('Error %s'   % result.error_count  )
            if status:
                status = ' '.join(status)
            else:
                status = 'none'
            return [
                ('Start Time', startTime),
                ('Duration', duration),
                ('Status', status),
            ]
       
       
        def generateReport(self, test, result):
            report_attrs = self.getReportAttributes(result)
            generator = 'HTMLTestRunner %s' % __version__
            stylesheet = self._generate_stylesheet()
            heading = self._generate_heading(report_attrs)
            report = self._generate_report(result)
            ending = self._generate_ending()
            output = self.HTML_TMPL % dict(
                title = saxutils.escape(self.title),
                generator = generator,
                stylesheet = stylesheet,
                heading = heading,
                report = report,
                ending = ending,
            )
            self.stream.write(output.encode('utf8'))
       
       
        def _generate_stylesheet(self):
            return self.STYLESHEET_TMPL
       
       
        def _generate_heading(self, report_attrs):
            a_lines = []
            for name, value in report_attrs:
                line = self.HEADING_ATTRIBUTE_TMPL % dict(
                        name = saxutils.escape(name),
                        value = saxutils.escape(value),
                    )
                a_lines.append(line)
            heading = self.HEADING_TMPL % dict(
                title = saxutils.escape(self.title),
                parameters = ''.join(a_lines),
                description = saxutils.escape(self.description),
            )
            return heading
       
       
        def _generate_report(self, result):
            rows = []
            sortedResult = self.sortResult(result.result)
            for cid, (cls, cls_results) in enumerate(sortedResult):
                # subtotal for a class
                np = nf = ne = 0
                for n,t,o,e in cls_results:
                    if n == 0: np += 1
                    elif n == 1: nf += 1
                    else: ne += 1
       
                # format class description
                if cls.__module__ == "__main__":
                    name = cls.__name__
                else:
                    name = "%s.%s" % (cls.__module__, cls.__name__)
                doc = cls.__doc__ and cls.__doc__.split("
    ")[0] or ""
                desc = doc and '%s: %s' % (name, doc) or name
       
                row = self.REPORT_CLASS_TMPL % dict(
                    style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
                    desc = desc,
                    count = np+nf+ne,
                    Pass = np,
                    fail = nf,
                    error = ne,
                    cid = 'c%s' % (cid+1),
                )
                rows.append(row)
       
                for tid, (n,t,o,e) in enumerate(cls_results):
                    self._generate_report_test(rows, cid, tid, n, t, o, e)
       
            report = self.REPORT_TMPL % dict(
                test_list = ''.join(rows),
                count = str(result.success_count+result.failure_count+result.error_count),
                Pass = str(result.success_count),
                fail = str(result.failure_count),
                error = str(result.error_count),
            )
            return report
       
       
        def _generate_report_test(self, rows, cid, tid, n, t, o, e):
            # e.g. 'pt1.1', 'ft1.1', etc
            has_output = bool(o or e)
            tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
            name = t.id().split('.')[-1]
            doc = t.shortDescription() or ""
            desc = doc and ('%s: %s' % (name, doc)) or name
            tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
       
            # o and e should be byte string because they are collected from stdout and stderr?
            if isinstance(o,str):
                # TODO: some problem with 'string_escape': it escape 
     and mess up formating
                # uo = unicode(o.encode('string_escape'))
                uo = o.decode('latin-1')
            else:
                uo = o
            if isinstance(e,str):
                # TODO: some problem with 'string_escape': it escape 
     and mess up formating
                # ue = unicode(e.encode('string_escape'))
                ue = e.decode('latin-1')
            else:
                ue = e
       
            script = self.REPORT_TEST_OUTPUT_TMPL % dict(
                id = tid,
                output = saxutils.escape(uo+ue),
            )
       
            row = tmpl % dict(
                tid = tid,
                Class = (n == 0 and 'hiddenRow' or 'none'),
                style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
                desc = desc,
                script = script,
                status = self.STATUS[n],
            )
            rows.append(row)
            if not has_output:
                return
       
        def _generate_ending(self):
            return self.ENDING_TMPL
       
       
    ##############################################################################
    # Facilities for running tests from the command line
    ##############################################################################
       
    # Note: Reuse unittest.TestProgram to launch test. In the future we may
    # build our own launcher to support more specific command line
    # parameters like test title, CSS, etc.
    class TestProgram(unittest.TestProgram):
        """
        A variation of the unittest.TestProgram. Please refer to the base
        class for command line parameters.
        """
        def runTests(self):
            # Pick HTMLTestRunner as the default test runner.
            # base class's testRunner parameter is not useful because it means
            # we have to instantiate HTMLTestRunner before we know self.verbosity.
            if self.testRunner is None:
                self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
            unittest.TestProgram.runTests(self)
       
    main = TestProgram
       
    ##############################################################################
    # Executing this module from the command line
    ##############################################################################
       
    if __name__ == "__main__":
        main(module=None)
    

      记住,这是一个.py文件。配置完毕之后再次去试一下“import HTMLTestRunner”,如果不报错,恭喜你,成功配置文件成功。

    13.2.3 HTMLTestRunner的使用

    首先我们要知道我们定要运行一个case,那么我们是不是首先要将我们的case都封装起来,然后才能去执行呢?

    so,这里我们需要讲到开发语言中经常用到的一个关键字 class,就是我们经常说的类,python里面类的定义很简单,class className():这就是一个类的定义。

    下面我们来看一个最最最简单的HTMLTestRunner的测试模版是怎么样的

    # -*- coding: utf-8 -*-
    import unittest
    #引入要测试的文件,就是引入我们测试类所属文件
    import testMycase
    import HTMLTestRunner   
    suite = unittest.TestSuite()
    #引入测试的类,因为我们的类在testMycase这个文件下,所以就直接文件名.类名
    suite.addTest(unittest.makeSuite(testMycase.appiumTest))
    #定义报告路径
    filename = 'test.html'
    #定义报告文件权限,wb,表示有读写权限
    fp = file(filename,'wb')
    runner = HTMLTestRunner.HTMLTestRunner(
            stream = fp,
            title ='appiumTest',
            description = '测试报告')
    #执行测试
    runner.run(suite)
    #关闭文件,否则会无法生成文件
    fp.close()
    

      

    上面的代码就是我们测试报告生成的一个简要模版,大家只需要将自己需要测试的类引入到这个里面就可以了,当然也是可以直接在方法中去运行的,但是为了方便,这里给大家说了另外一个知识。赶快下去动手吧

  • 相关阅读:

    ATM三层架构思路
    一个项目的从无到有
    re模块
    logging模块
    物联网公共安全平台软件体系架构
    本科生怎样发表自己的论文
    Cloud Native 云化架构阅读笔记
    实验5 Spark SQL编程初级实践
    云计算环境下计算机软件系统架构分析
  • 原文地址:https://www.cnblogs.com/Mushishi_xu/p/7797902.html
Copyright © 2020-2023  润新知