基于python接口测试模板带图表的HTMLTestRunner

基于HTMLTestRunner 修改的接口测试报告,带有柱状图和饼图,有需要做接口测试的朋友直接拿去 图表是基于js Mixed Line and Bar 插件

官网地址:http://echarts.baidu.com      大家可以参考来进行自定义自己想要的模板,本次修改点添加了 在HTMLTestRunner添加了图表视图 “ECHARTS_SCRIPT”,大家想要修改在此处添加JS图表源码,知识乐于分享,希望不喜勿喷。如果有好的意见欢迎大家提出。

源码L

"""A TestRunner for use with the Python unit testing framework. Itgenerates 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 TungAll rights reserved.

Redistribution and use in source and binary forms, with or withoutmodification, are permitted provided that the following conditions aremet:

* 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 "ASIS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITEDTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR APARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNEROR 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, ORPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDINGNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."""

# URL: http://tungwaiyip.info/software/HTMLTestRunner.htmlimport io

__author__ = "Wai Yip Tung"__version__ = "0.9.1"

"""Change HistoryVersion 0.9.1* 用Echarts添加执行情况统计图 (灰蓝)

Version 0.9.0* 改成Python 3.x (灰蓝)

Version 0.8.3* 使用 Bootstrap稍加美化 (灰蓝)* 改为中文 (灰蓝)

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 datetimeimport sysimport timeimport unittestfrom xml.sax import saxutilsPY3K = (sys.version_info[0] > 2)if PY3K:    import io as StringIOelse:    import StringIOimport copy

# ------------------------------------------------------------------------# 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: ‘通过‘,        1: ‘失败‘,        2: ‘错误‘,    }

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    <link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">    <!-- <script type="text/javascript" src="https://cdn.bootcss.com/echarts/3.8.5/echarts.common.js"></script>-->    <script src="https://cdn.bootcss.com/echarts/3.8.5/echarts.common.min.js"></script></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,‘&amp;‘);        s = s.replace(/</g,‘&lt;‘);        s = s.replace(/>/g,‘&gt;‘);        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("\n");        d.write("<a href=‘javascript:window.close()‘>close</a>\n");        d.write("</pre>\n");        d.close();    }    */    --></script>

<div id="div_base">        %(heading)s        %(report)s        %(ending)s        %(chart_script)s

</div></body></html>"""  # variables: (title, generator, stylesheet, heading, report, ending, chart_script)

ECHARTS_SCRIPT = """        <script type="text/javascript">        var myChartline = echarts.init(document.getElementById(‘chartline‘));        var optionline = {                tooltip: {                trigger: ‘axis‘,                axisPointer: {                    type: ‘cross‘,                    crossStyle: {                        color: ‘#999‘                    }                }            },            toolbox: {                feature: {                    dataView: {show: true, readOnly: false},                    magicType: {show: true, type: [‘line‘, ‘bar‘]},                    restore: {show: true},                    saveAsImage: {show: true}                }            },            legend: {                data:[‘错误‘,‘成功‘,‘失败‘]            },            xAxis: [                {                    type: ‘category‘,                    data: [‘ 第一次‘,‘第二次‘,‘第三次‘,‘第四次‘,‘第五次‘,‘第六次‘,‘第七次‘,‘第八次‘,‘第九次‘,‘第十次‘],                    axisPointer: {                        type: ‘shadow‘                    }                }            ],            yAxis: [                {                    type: ‘value‘,                    name: ‘百分比‘,                    min: 0,                    max: 100,                    interval: 20,                    axisLabel: {                        formatter: ‘{value} %%‘                    }                },                {                    type: ‘value‘,                    name: ‘成功率‘,                    min: 0,                    max: 10,                    interval: 2,                    axisLabel: {                        formatter: ‘{value} %%‘                    }                }            ],            series: [                {                    name:‘成功‘,                    type:‘bar‘,                    data:[%(Pass)s]                //data:[2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]                },                {                    name:‘失败‘,                    type:‘bar‘,                    data:[%(fail)s]                },                {                    name:‘错误‘,                    type:‘line‘,                    yAxisIndex: 1,                    data:[%(error)s]                    //data:[2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]                }            ]        };        myChartline.setOption(optionline);        console.log(%(fail)s,%(Pass)s,%(error)s)            // 基于准备好的dom,初始化echarts实例            var myChart = echarts.init(document.getElementById(‘chart‘));

// 指定图表的配置项和数据            var option = {                title : {                    text: ‘测试执行情况‘,                    x:‘center‘                },                tooltip : {                    trigger: ‘item‘,                    formatter: "{a} <br/>{b} : {c} ({d}%%)"                },                legend: {                    orient: ‘vertical‘,                    left: ‘left‘,                    data: [‘通过‘,‘失败‘,‘错误‘]                },                series : [                    {                        name: ‘测试执行情况‘,                        type: ‘pie‘,                        radius : ‘60%%‘,                        center: [‘50%%‘, ‘60%%‘],                        data:[                            {value:%(Pass)s, name:‘通过‘},                            {value:%(fail)s, name:‘失败‘},                            {value:%(error)s, name:‘错误‘}                        ],                        itemStyle: {                            emphasis: {                                shadowBlur: 10,                                shadowOffsetX: 0,                                shadowColor: ‘rgba(0, 0, 0, 0.5)‘                            }                        }                    }                ]            };

// 使用刚指定的配置项和数据显示图表。            myChart.setOption(option);        </script>    """

# variables: (Pass, fail, error)

# ------------------------------------------------------------------------    # 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         { white-space: pre-wrap;word-wrap: break-word; }

/* -- 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: 2ex;        margin-bottom: 3ex;    }

/* -- 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;        /* */        font-family: "Lucida Console", "Courier New", Courier, monospace;        text-align: left;        font-size: 8pt;        /* width: 500px;*/    }

}    /* -- report ------------------------------------------------------------------------ */    #show_detail_line {        margin-top: 3ex;        margin-bottom: 1ex;    }    #result_table {        width: 99%;    }    #header_row {        font-weight: bold;        color: #303641;

}    #total_row  { font-weight: bold; }    .passClass  {  }    .failClass  { background-color: #ffefa4; }    .errorClass {  }    .passCase   { color: #6c6; }    .failCase   { color: #FF6600; font-weight: bold; }    .errorCase  { color: #c00; font-weight: bold; }    .hiddenRow  { display: none; }    .testcase   { margin-left: 2em; }

/* -- ending ---------------------------------------------------------------------- */    #ending {    }

#div_base {                position:absolute;                top:0%;                left:5%;                right:5%;                width: auto;                height: auto;                margin: -15px 0 0 0;    }</style>"""

# ------------------------------------------------------------------------    # Heading    #

HEADING_TMPL = """    <div class=‘page-header‘>        <h1>%(title)s</h1>    %(parameters)s    </div>    <!--<div style="float: left;width:50%%;"><p class=‘description‘>%(description)s</p></div>-->    <div id="chartline" style="width:50%%;height:400px;float:left;"></div>    <div id="chart"style="width:50%%;height:400px;float:right;"></div>"""  # variables: (title, parameters, description)

HEADING_ATTRIBUTE_TMPL = """<p class=‘attribute‘><strong>%(name)s:</strong> %(value)s</p>"""  # variables: (name, value)

# ------------------------------------------------------------------------    # Report    #

REPORT_TMPL = """    <div class="btn-group btn-group-sm">         <!-- <button class="btn btn-default" onclick=‘javascript:showCase(0)‘>总结</button> -->        <!--<button class="btn btn-default" onclick=‘javascript:showCase(1)‘>失败</button>-->        <!--<button class="btn btn-default" onclick=‘javascript:showCase(2)‘>全部</button>-->        <a class="btn btn-primary" onclick=‘javascript:showCase(0)‘>概要 %(passrate)s </a>        <a class="btn btn-warning" onclick=‘javascript:showCase(4)‘>错误 %(error)s </a>        <a class="btn btn-danger" onclick=‘javascript:showCase(1)‘>失败  %(fail)s </a>        <a class="btn btn-success" onclick=‘javascript:showCase(2)‘>通过 %(Pass)s </a>        <a class="btn btn-info" onclick=‘javascript:showCase(3)‘>所有 %(count)s </a>  

</div>    <p></p>    <table id=‘result_table‘ class="table table-bordered">        <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>测试套件/测试用例</td>            <td>总数</td>            <td>通过</td>            <td>失败</td>            <td>错误</td>            <td>查看</td>            <th>错误截图</th>        </tr>        %(test_list)s        <tr id=‘total_row‘>            <td>总计</td>            <td>%(count)s</td>            <td>%(Pass)s</td>            <td>%(fail)s</td>            <td>%(error)s</td>            <td>&nbsp;</td>            <th>&nbsp;</th>        </tr>    </table>"""  # variables: (test_list, count, Pass, fail, error)

REPORT_CLASS_TMPL = u"""    <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)">详情</a></td>        <td>&nbsp;</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">        <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)    IMG_TMPL = r"""            <a href="#"  onclick="show_img(this)">显示截图</a>        <div align="center" class="screenshots"  style="display:none;z-index:2000">            <a class="close_shots"  href="#"   onclick="hide_img(this)"></a>            %(imgs)s            <div class="imgyuan"></div>        </div>        """    # ------------------------------------------------------------------------    # ENDING    #

ENDING_TMPL = """<div id=‘ending‘>&nbsp;</div>"""

# -------------------- The end of the Template class -------------------    def __getattribute__(self, item):        value = object.__getattribute__(self, item)        if PY3K:            return value        else:            if isinstance(value, str):                return value.decode("utf-8")            else:                return value

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 = []        self.subtestlist = []

def startTest(self, test):        TestResult.startTest(self, test)        # just one buffer for both stdout and stderr        self.outputBuffer = io.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):        if test not in self.subtestlist:            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(‘\n‘)            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(‘\n‘)        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 not getattr(test, "driver", ""):        #     pass        # else:        #     try:        #         driver = getattr(test, "driver")        #         test.imgs.append(driver.get_screenshot_as_base64())        #     except Exception as e:        #         pass        if self.verbosity > 1:            sys.stderr.write(‘F  ‘)            sys.stderr.write(str(test))            sys.stderr.write(‘\n‘)        else:            sys.stderr.write(‘F‘)

def addSubTest(self, test, subtest, err):        if err is not None:            if getattr(self, ‘failfast‘, False):                self.stop()            if issubclass(err[0], test.failureException):                self.failure_count += 1                errors = self.failures                errors.append((subtest, self._exc_info_to_string(err, subtest)))                output = self.complete_output()                self.result.append((1, test, output + ‘\nSubTestCase Failed:\n‘ + str(subtest),                                    self._exc_info_to_string(err, subtest)))                if self.verbosity > 1:                    sys.stderr.write(‘F  ‘)                    sys.stderr.write(str(subtest))                    sys.stderr.write(‘\n‘)                else:                    sys.stderr.write(‘F‘)            else:                self.error_count += 1                errors = self.errors                errors.append((subtest, self._exc_info_to_string(err, subtest)))                output = self.complete_output()                self.result.append(                    (2, test, output + ‘\nSubTestCase Error:\n‘ + str(subtest), self._exc_info_to_string(err, subtest)))                if self.verbosity > 1:                    sys.stderr.write(‘E  ‘)                    sys.stderr.write(str(subtest))                    sys.stderr.write(‘\n‘)                else:                    sys.stderr.write(‘E‘)            self._mirrorOutput = True        else:            self.subtestlist.append(subtest)            self.subtestlist.append(test)            self.success_count += 1            output = self.complete_output()            self.result.append((0, test, output + ‘\nSubTestCase Pass:\n‘ + str(subtest), ‘‘))            if self.verbosity > 1:                sys.stderr.write(‘ok ‘)                sys.stderr.write(str(subtest))                sys.stderr.write(‘\n‘)            else:                sys.stderr.write(‘.‘)

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(‘\nTime Elapsed: %s‘ % (self.stopTime-self.startTime), file=sys.stderr)

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 cls not in rmap:                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(‘通过 %s‘ % result.success_count)        if result.failure_count: status.append(‘失败 %s‘ % result.failure_count)        if result.error_count:   status.append(‘错误 %s‘ % result.error_count  )        if status:            status = ‘ ‘.join(status)        else:            status = ‘none‘        return [            (‘开始时间‘, startTime),            (‘运行时长‘, duration),            (‘状态‘, 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()        chart = self._generate_chart(result)        output = self.HTML_TMPL % dict(            title = saxutils.escape(self.title),            generator = generator,            stylesheet = stylesheet,            heading = heading,            report = report,            ending = ending,            chart_script = chart,

)        self.stream.write(output.encode(‘utf8‘))        #self.stream.write(output)    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("\n")[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),            # 统计全部的            passrate=str("%.2f%%" % (float(result.success_count) /                                     float(result.success_count + result.failure_count + result.error_count) * 100)                         ),        )        return report

def _generate_chart(self, result):        chart = self.ECHARTS_SCRIPT % dict(            Pass=str(result.success_count),            fail=str(result.failure_count),            error=str(result.error_count),        )        return chart

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]        if self.verbosity > 1:            doc = t._testMethodDoc or ‘‘        else:            doc = ""

# doc = t.shortDescription() or ""        desc = doc and (‘%s: %s‘ % (name, doc)) or name        if not PY3K:            if isinstance(desc, str):                desc = desc.decode("utf-8")        tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL        tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL        if isinstance(o, str):            # uo = unicode(o.encode(‘string_escape‘))            if PY3K:                uo = o            else:                uo = o.decode(‘utf-8‘, ‘ignore‘)        else:            uo = o        if isinstance(e, str):            # ue = unicode(e.encode(‘string_escape‘))            if PY3K:                ue = e            elif e.find("Error") != -1 or e.find("Exception") != -1:                es = e.decode(‘utf-8‘, ‘ignore‘).split(‘\n‘)                es[-2] = es[-2].decode(‘unicode_escape‘)                ue = u"\n".join(es)            else:                ue = e.decode(‘utf-8‘, ‘ignore‘)        else:            ue = e        script = self.REPORT_TEST_OUTPUT_TMPL % dict(            id=tid,            output=saxutils.escape(o+e),        )        if getattr(t,‘imgs‘,[]):            # 判断截图列表,如果有则追加            tmp = u""            for i, img in enumerate(t.imgs):                if i==0:                    tmp+=""" <img src="data:image/jpg;base64,%s" style="display: block;" class="img"/>\n""" % img                else:                    tmp+=""" <img src="data:image/jpg;base64,%s" style="display: none;" class="img"/>\n""" % img            imgs = self.IMG_TMPL % dict(imgs=tmp)        else:            imgs = u"""无截图"""

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],            img=imgs,        )        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)

原文地址:https://www.cnblogs.com/mahaining/p/9697008.html

时间: 2024-10-13 23:50:16

基于python接口测试模板带图表的HTMLTestRunner的相关文章

基于Python Requests的数据驱动的HTTP接口测试

发表于:2017-8-30 11:56  作者:顾翔   来源:51Testing软件测试网原创 http://www.51testing.com/html/69/n-3720769-2.html 1.测试金字塔 图 1软件测试金字塔 图 1是Main Cohn提出的软件测试金字塔,他认为作为一个测试工程师应该把大量的工作花在单元测试和接口测试,而其余的发在UI测试以及探索式测试.纵然,单元测试的优点很突出,它接近于代码本身,运行速度快,开发可以一边写产品代码一边写单元测试代码,一旦在单元测试中

基于Python的接口测试框架实例

文章来源:http://www.jb51.net/article/96481.htm 下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 背景 最近公司在做消息推送,那么自然就会产生很多接口,测试的过程中需要调用接口,我就突然觉得是不是可以自己写一个测试框架? 说干就干,由于现有的接口测试工具Jmeter.SoupUI等学习周期有点长,干脆自己写一个吧,不求人,所有功能自己都能一清二楚. 当然,写工具造轮子只是

性能测试 基于Python结合InfluxDB及Grafana图表实时采集Linux多主机性能数据

基于Python结合InfluxDB及Grafana图表实时采集Linux多主机性能数据   by:授客 QQ:1033553122 实现功能 测试环境 环境搭建 使用前提 使用方法 运行程序 效果展示 实现功能 无需在被监控主机上安装代理,一键对Linux远程服务器不同主机执行性能监控.性能数据采集命令,并实时展示 支持跨堡垒机收集实时性能数据(注:定制化开发,非通用) 支持docker容器(因为程序实现是从docker容器内部获取性能数据,所以目前仅支持 CPU,内存,I/O) 使用前提 可

简单实现接口自动化测试(基于python+unittest)

简单实现接口自动化测试(基于python+unittest) 简介 本文通过从Postman获取基本的接口测试Code简单的接口测试入手,一步步调整优化接口调用,以及增加基本的结果判断,讲解Python自带的Unittest框架调用,期望各位可以通过本文对接口自动化测试有一个大致的了解. 引言 为什么要做接口自动化测试? 在当前互联网产品迭代频繁的背景下,回归测试的时间越来越少,很难在每个迭代都对所有功能做完整回归.但接口自动化测试因其实现简单.维护成本低,容易提高覆盖率等特点,越来越受重视.

基于Python的数据分析(1):配置安装环境

数据分析是一个历史久远的东西,但是直到近代微型计算机的普及,数据分析的价值才得到大家的重视.到了今天,数据分析已经成为企业生产运维的一个核心组成部分. 据我自己做数据分析的经验来看,目前数据分析按照使用工具可以分为大体四类: 基于Excel的数据分析,Excel自带的函数.数据透视表.宏等功能对于数据分析来说十分适用且好用:基于matlib.SAS.SPSS等专业统计软件,我自己用过一段时间的SAS,觉得功能十分全面,但是作为程序员使用又觉得限制太多不够自由:基于SQL+数据库的数据分析,这一类

Flask之旅《Flask Web开发:基于Python的Web应用开发实战》学习笔记

<Flask Web开发:基于Python的Web应用开发实战> 点击上方的"目录"快速到达哦! 虽然简单的网站(Flask+Python+SAE)已经上线,但只是入门.开发大型网站,系统地学习一遍还是有必要的. 1 虚拟环境 2016-6-8 书上介绍了 virtualenv,每个venv都会拷贝一份packages到项目 /venv目录. virtualenv venv venv\Scripts\activate.bat (venv) $ pip freeze >

基于Python的Web应用开发实践总结

基于Python的Web应用开发学习总结 项目地址 ??本次学习采用的是Flask框架.根据教程开发个人博客系统.博客界面如图所示. 整个学习过程收获很多,以下是学习总结. 1.virtualenv virtualenv是第三方工具,用于创建虚拟环境.可以为每个程序单独创建虚拟环境,每个虚拟环境互不影响,并且不影响全局的环境,也就是说在不同的虚拟环境里可以使用不同版本的python和插件等.使用虚拟环境的另一好处是,不需要管理员权限. 以下是virtualenv的安装和使用: 1.检查是否安装了

如何简单实现接口自动化测试(基于 python) 原博主地址https://blog.csdn.net/gitchat/article/details/77849725

如何简单实现接口自动化测试(基于 python) 2017年09月05日 11:52:25 阅读数:9904 GitChat 作者:饿了么技术社区 原文:如何简单实现接口自动化测试(基于 python) 关注微信公众号:GitChat 技术杂谈 ,这里一本正经的讲技术 一.简介 本文从一个简单的登录接口测试入手,一步步调整优化接口调用姿势,然后简单讨论了一下接口测试框架的要点,最后介绍了一下我们目前正在使用的接口测试框架pithy.期望读者可以通过本文对接口自动化测试有一个大致的了解. 二.引言

Python接口测试实战3(下)- unittest测试框架

如有任何学习问题,可以添加作者微信:lockingfree 课程目录 Python接口测试实战1(上)- 接口测试理论 Python接口测试实战1(下)- 接口测试工具的使用 Python接口测试实战2 - 使用Python发送请求 Python接口测试实战3(上)- Python操作数据库 Python接口测试实战3(下)- unittest测试框架 Python接口测试实战4(上) - 接口测试框架实战 Python接口测试实战4(下) - 框架完善:用例基类,用例标签,重新运行上次失败用例