一、直接使用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,'&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">
// 基于准备好的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>&nbsp;</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'>&nbsp;</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使用教程的更多相关文章

  1. 18年selenium3+python3+unittest自动化测试教程(下)

    第六章 自动化测试进阶实战篇幅 1.自动化测试实战进阶之网页单选性别资料实战 简介:讲解使用selenium修改input输入框和单选框 2.自动化测试之页面常见弹窗处理 简介:讲解使用seleniu ...

  2. 18年selenium3+python3+unittest自动化测试教程(上)

    第一章 自动化测试课程介绍和课程大纲 1.自动化测试课程介绍 简介:讲解什么是自动化测试和课程大纲讲解,课程需要的基础和学后的水平 python3.7+selenium3 pycharm 第二章自动化 ...

  3. Python3.x爬虫教程:爬网页、爬图片、自己主动登录

    林炳文Evankaka原创作品. 转载请注明出处http://blog.csdn.net/evankaka 摘要:本文将使用Python3.4爬网页.爬图片.自己主动登录.并对HTTP协议做了一个简单 ...

  4. python3+unittest+HTMLTestRunner

    参考博客1 参考博客2 python3版HTMLTestRunner.py见博客园‘链接’(已经上传到博客园) import unittest class operatinon_unittest(un ...

  5. python3 unittest框架失败重跑加截图支持python2,python3

    github源码地址下载:https://github.com/GoverSky/HTMLTestRunner_cn.git 解压文件后取出/HTMLTestRunner_cn.py文件丢进C:\Py ...

  6. [教程]Tensorflow + win10 + CPU + Python3.6+ 安装教程

    由于各种原因,清华镜像源已经彻底挂掉了,但是目前网上的各种教程基本上都是采取设置清华镜像源来加快下载速度,所以这给小白带来了很大的困扰!这里我将通过合理上网工具来直接下载源镜像. 注意:本次教程适用于 ...

  7. python3+unittest参考

    Python3+Selenium+unittest自动化UI测试框架:https://www.cnblogs.com/G2Bent/p/8376001.html unittest --- 单元测试框架 ...

  8. Python Unittest简明教程

    1 概述 单元测试框架是一种软件测试方法,通过来测试源代码中的各个单元,例如类,方法等,以确定它们是否符合要求.直观上来说,可以将单元视为最小的可测试部分.单元测试是程序员在开发过程中创建的短代码片段 ...

  9. python3 字符串str 教程

    字符串可以用单引号或双引号来创建. Python 不支持单字符类型,单字符也在Python也是作为一个字符串使用. 例: var1 = 'Hello World!' var2 = "Pyth ...

随机推荐

  1. liunx驱动----异步通知

    查询:消耗资源 中断:read 一直要去读 poll :指定起始时间 异步通知 signal 测试程序 include <stdio.h> include <signal.h> ...

  2. jenkins+git+maven

    这个有参考:https://blog.csdn.net/xlgen157387/article/details/50353317 主要针对一些错误记录一下 1.如果要不想用系统的jdk等,可以在全局工 ...

  3. 认识volatile的工作原理

    前言 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”.可见性的意思是当 ...

  4. linux随

    Linux 1991芬兰大学生linus 安全 开源 免费 可靠 (堡垒机)(跳转机) 多用户 多任务 处理并发及速度比windows好很多 命令:ifconfig pwd ls -a -l -t(时 ...

  5. create-react-app不暴露配置设置proxy代理

    此方法可以在不暴露配置的情况下直接设置代理,非常便捷 在package.json里添加 "proxy":"http://institute.dljy.lzdev" ...

  6. 《视觉SLAM十四讲课后作业》第一讲

    1. 如何在 Ubuntu 中安装软件(命令⾏界⾯)?它们通常被安装在什么地⽅? 答:一般有两种安装方式(1)apt-get install (2)dpkg -i package.deb.系统软件一般 ...

  7. 【题解】Luogu P3674 小清新人渣的本愿

    原题传送门 这题还算简单(我记得我刚学oi时就来写这题,然后暴力都爆零了) 看见无修改,那么这题应该是莫队 维护两个bitset,第二个是第一个的反串,bitset内维护每个数字是否出现过 第一种操作 ...

  8. Excel导出采用mvc的ExcelResult继承遇到的问题Npoi导出

    #region 构建Excel文档 //创建Excel文件的对象 NPOI.HSSF.UserModel.HSSFWorkbook book = new NPOI.HSSF.UserModel.HSS ...

  9. Bigger-Mai 养成计划,Python基础巩固三

    1.集合的基本操作(set)集合是一个无序的,不重复的元素集合,他的主要作用是去重:把一个列表变为集合就自动去重了.关系测试:测试两组数据之间的交集,差集,并集等关系 list_1 = [1,4,6, ...

  10. python中import与from方法总结

    一.模块&包简介 模块:所谓模块就是一个.py文件,用来存放变量,方法的文件,便于在其他python文件中导入(通过import或from). 包(package): 包是更大的组织单位,用来 ...