Python3+unittest使用教程
一、直接使用TestCase
注意所有测试方法都需要以test开头。代码如下:
- import unittest
- class Test1(unittest.TestCase):
- @classmethod
- def setUpClass(self):
- print("execute setUpClass")
- @classmethod
- def tearDownClass(self):
- print("execute tearDownClass")
- def setUp(self):
- print("execute setUp")
- def tearDown(self):
- print("execute tearDown")
- def test_one(self):
- print('execute test_one')
- self.assertTrue('FOO'.isupper())
- def test_two(self):
- print('execute test_two')
- if __name__ == '__main__':
- unittest.main()
执行如下:
二、使用TestSuite
直接写TestCase执行时是按字母排序的顺序执行的,如果要设定测试用例的执行先后顺序则需要将TestCase封装到TestSuite。代码如下:
- import unittest
- class Test2(unittest.TestCase):
- @classmethod
- def setUpClass(self):
- print("execute setUpClass")
- @classmethod
- def tearDownClass(self):
- print("execute tearDownClass")
- def setUp(self):
- print("execute setUp")
- def tearDown(self):
- print("execute tearDown")
- def test_one(self):
- print('execute test_one')
- self.assertTrue('FOO'.isupper())
- def test_two(self):
- print('execute test_two')
- if __name__ == '__main__':
- suite = unittest.TestSuite()
- # Test2是要测试的类名,test_two是要执行的测试方法
- suite.addTest(Test2("test_two"))
- suite.addTest(Test2("test_one"))
- runner = unittest.TextTestRunner()
- runner.run(suite)
执行如下,可以看到先添加的test_two先执行:
三、使用HTMLTestRunner
我们可能还会希望生成一个简单的HTML报告,可使用HTMLTestRunner实现。但pypi和官网上最新的都是只支持python2.x的0.8.2版本。可将以下代码自行保存成HTMLTestRunner.py放到自己项目目录下
- # -*- coding: utf-8 -*-
- """
- 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.9.1"
- """
- Change History
- Version 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 datetime
- import sys
- import io
- 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: u'通过',
- 1: u'失败',
- 2: u'错误',
- }
- 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"/>
- <link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
- <script src="https://cdn.bootcss.com/echarts/3.8.5/echarts.common.min.js"></script>
- <!-- <script type="text/javascript" src="js/echarts.common.min.js"></script> -->
- %(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("\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">
- // 基于准备好的dom,初始化echarts实例
- var myChart = echarts.init(document.getElementById('chart'));
- // 指定图表的配置项和数据
- var option = {
- title : {
- text: '测试执行情况',
- x:'center'
- },
- tooltip : {
- trigger: 'item',
- formatter: "{a} <br/>{b} : {c} ({d}%%)"
- },
- color: ['#95b75d', 'grey', '#b64645'],
- 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: Microsoft YaHei,Consolas,arial,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;
- /*background-color: #E6E6D6; */
- 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;
- background-color: #ebebeb;
- }
- #total_row { font-weight: bold; }
- .passClass { background-color: #bdedbc; }
- .failClass { background-color: #ffefa4; }
- .errorClass { background-color: #ffc9c9; }
- .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="chart" style="width:50%%;height:400px;float:left;"></div>
- """ # variables: (title, parameters, description)
- HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
- """ # variables: (name, value)
- # ------------------------------------------------------------------------
- # Report
- #
- REPORT_TMPL = u"""
- <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>
- </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>
- </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> </td>
- </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>
- </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)
- # ------------------------------------------------------------------------
- # 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 = []
- 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 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(u'通过 %s' % result.success_count)
- if result.failure_count: status.append(u'失败 %s' % result.failure_count)
- if result.error_count: status.append(u'错误 %s' % result.error_count )
- if status:
- status = ' '.join(status)
- else:
- status = 'none'
- return [
- (u'开始时间', startTime),
- (u'运行时长', duration),
- (u'状态', 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("gg".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("\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),
- )
- 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]
- 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
- script = self.REPORT_TEST_OUTPUT_TMPL % dict(
- id=tid,
- output=saxutils.escape(o+e),
- )
- 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)
代码如下:
- import unittest
- import HTMLTestRunner
- class Test3(unittest.TestCase):
- @classmethod
- def setUpClass(self):
- print("execute setUpClass")
- @classmethod
- def tearDownClass(self):
- print("execute tearDownClass")
- def setUp(self):
- print("execute setUp")
- def tearDown(self):
- print("execute tearDown")
- def test_one(self):
- print('execute test_one')
- self.assertTrue('FOO'.isupper())
- def test_two(self):
- print('execute test_two')
- if __name__ == '__main__':
- suite = unittest.TestSuite()
- # Test3是要测试的类名,test_one是要执行的测试方法
- suite.addTest(Test3("test_one"))
- suite.addTest(Test3("test_two"))
- # 实践中发现执行时的当前路径,不一定是此文件所在的文件夹,所以使用绝对路径
- # print(f"{os.getcwd()}")
- filename = 'F:\\PycharmProjects\\test3\\apptestresult.html'
- fb = open(filename, 'wb')
- runner = HTMLTestRunner.HTMLTestRunner(stream=fb, title="测试HTMLTestRunner", description="测试HTMLTestRunner")
- runner.run(suite)
- fb.close()
执行如下:
报告如下:
四、HTMLTestRunner未生成报告问题处理
4.1 pytest干扰
如果python环境中安装了pytest,那么默认运行时会是Run 'py.test for'(如下图所示),这时if __name__ == "__main__"下的语句是不被执行的所以报告肯定不会生成(原理未知)。
些时需要依次点开pycharm菜单----Run----Edit Configurations...自行添加一个运行配置,如下图
4.2 路径问题
一是注意自己把报告输出到了哪个位置,特别是如果有几个地方都会输出报告时不要弄混了,搞得自己在浏览器中刷新半天没看到有变化,认为没生成报告。
二是实践中通过os.getcwd()发现python运行时的目录不一定是当前目录,所以如果报告使用相对路径然后又没看到有报告生成,那就注意看os.getcwd()返回的运行路径是不是当前文件夹。
参考:
https://docs.python.org/3/library/unittest.html
https://www.cnblogs.com/youreyebows/p/7867508.html
Python3+unittest使用教程的更多相关文章
- 18年selenium3+python3+unittest自动化测试教程(下)
第六章 自动化测试进阶实战篇幅 1.自动化测试实战进阶之网页单选性别资料实战 简介:讲解使用selenium修改input输入框和单选框 2.自动化测试之页面常见弹窗处理 简介:讲解使用seleniu ...
- 18年selenium3+python3+unittest自动化测试教程(上)
第一章 自动化测试课程介绍和课程大纲 1.自动化测试课程介绍 简介:讲解什么是自动化测试和课程大纲讲解,课程需要的基础和学后的水平 python3.7+selenium3 pycharm 第二章自动化 ...
- Python3.x爬虫教程:爬网页、爬图片、自己主动登录
林炳文Evankaka原创作品. 转载请注明出处http://blog.csdn.net/evankaka 摘要:本文将使用Python3.4爬网页.爬图片.自己主动登录.并对HTTP协议做了一个简单 ...
- python3+unittest+HTMLTestRunner
参考博客1 参考博客2 python3版HTMLTestRunner.py见博客园‘链接’(已经上传到博客园) import unittest class operatinon_unittest(un ...
- python3 unittest框架失败重跑加截图支持python2,python3
github源码地址下载:https://github.com/GoverSky/HTMLTestRunner_cn.git 解压文件后取出/HTMLTestRunner_cn.py文件丢进C:\Py ...
- [教程]Tensorflow + win10 + CPU + Python3.6+ 安装教程
由于各种原因,清华镜像源已经彻底挂掉了,但是目前网上的各种教程基本上都是采取设置清华镜像源来加快下载速度,所以这给小白带来了很大的困扰!这里我将通过合理上网工具来直接下载源镜像. 注意:本次教程适用于 ...
- python3+unittest参考
Python3+Selenium+unittest自动化UI测试框架:https://www.cnblogs.com/G2Bent/p/8376001.html unittest --- 单元测试框架 ...
- Python Unittest简明教程
1 概述 单元测试框架是一种软件测试方法,通过来测试源代码中的各个单元,例如类,方法等,以确定它们是否符合要求.直观上来说,可以将单元视为最小的可测试部分.单元测试是程序员在开发过程中创建的短代码片段 ...
- python3 字符串str 教程
字符串可以用单引号或双引号来创建. Python 不支持单字符类型,单字符也在Python也是作为一个字符串使用. 例: var1 = 'Hello World!' var2 = "Pyth ...
随机推荐
- 2018-2019-2 20165215《网络对抗技术》Exp4 恶意代码分析
目录 实践目标 实践内容 基础问题回答 实验步骤 使用schtasks指令监控系统 使用sysmon工具监控系统 使用VirusTotal分析恶意软件 使用PEiD进行外壳检测 使用PE explor ...
- Git 爬坑路(从小白开始入门) ——(1)
通过git管理项目之前,需要先注册一个GitHub账号,方便在远程仓库进行项目管理. Git之项目在本地仓库的管理(从小白开始): 一.push到远程项目 1.在个人的GitHub账号中,创建一个远程 ...
- SQL查询数据时报错
在开发过程中如果查询报如下的错误: org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ...
- 【js】了解前端缓存,收获不止于此!
了解前端缓存,收获不止于此! 这次我们来讲一下关于前端缓存的问题.感谢赵欢同学提供doc素材. 首先,开局我画了一张图,你会对文章有一个大局了解. 今天讲的是前端缓存. 前端缓存有3大种:如图,分为H ...
- UML类图新手入门级介绍(转)
首先,看动物矩形框,它代表一个类(Class).类图分三层,第一层显示类的名称,如果是抽象类,则就用斜体显示.第二层是类的特性,通常就是字段和属性.第三层是类的操作,通常是方法或行为.前面的符号,+ ...
- msgid 属性
Android源码中的String.xml文件,msgid这个属性是干嘛的? 全局资源,方便引用.比如在布局的text和activity中用到.
- 2018年-2019年第二学期第七周C#学习个人总结
在本周我又学习了,第五章面向对象高级中的5.5异常和5.6命名空间和程序集.在5.5异常中我知道了异常层次结构:所有的异常类都继承自Exception类.由于发生了异常程序立即终止无法再继续向下执行. ...
- 2019充值新骗局手游折扣App靠谱程度一览表
随着互联网的快速发展,游戏产业也迎来了盛开的春天.特别是进入网络游戏时代后,来自世界各地的朋友,甚至来自地球村的朋友一起玩游戏.在这个阶段,游戏制作者还专注于设计副本,活动,皮肤和充值.无论你是否关心 ...
- Eclipse/MyEclipse导入导出注释模板
1.Eclipse/MyEclipse导入注释模板 (1)打开Eclipse/MyEclipse工具,打开或创建一个Java工程,点击菜单Window->Preferences弹出首选项设置窗口 ...
- 【新特性】JDK1.9
一.目录结构 JDK9具体目录结构如下所示: bin: 该目录包含所有的命令. conf: 包含用户可以编辑的配置文件,例如以前位于jre\lib 目录中的.properties 和 .policy ...