目录
4.5HTMLTestRunner.py类(下方有链接下载)
五、 *******************HTMLTestRunner.py文件分享********************
一、单元测试,集成测试,功能测试
**单元测试 **
颗粒度最小,一般由开发小组采用白盒方式来测试,主要测试单元是否符合“设计”;是指对软件中的最小可测试单元进行检查和验证
**集成测试 **
介于单元测试和系统测试之间,一般由开发小组采用白盒+黑盒的方法来测试,即验证“设计”又验证“需求”。主要用来测试模板与模板之间的接口,同时还要测试一些主要的业务功能。
**功能测试 **
颗粒度最大,一般由独立的测试小组采用黑盒的方式来测试,主要测试系统是否符合“需求规格说明书 问题:什么是白盒测试,什么是黑盒测试 主要应用于单元测试阶段,主要是对代码级别的测试,针对程序内部的逻辑结构。测试的手段有:语句覆盖、判定覆盖、条件覆盖、路径覆盖和条件组合覆盖 不考虑程序内部结构和逻辑结构,主要是测试系统的功能是否满足“需求规格说明书”。一般会有一个输入值和一个输出值,和期望值做比较。
二、Unittest重要组成
Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法(断言)和一些用例执行前的初始化操作。
**unittest中最核心的部分是:TestFixture、TestCase、TestSuite、TestRunner **
2.1TestFixture
**TestFixture:用于一个测试环境的准备和销毁还原。 **
当测试用例每次执行之前需要准备测试环境,每次测试完成后还原测试环境,比如执行前连接数据库、打开浏览器等,执行完成后需要还原数据库、关闭浏览器等操作。这时候就可以启用testfixture。
setUp():准备环境,执行每个测试用例的前置条件;
tearDown():环境还原,执行每个测试用例的后置条件;
setUpClass():必须使用@classmethod装饰器,所有case执行的前置条件,只运行一次;
tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次;
2.2TestCase:测试用例
**一个类class继承 unittest.TestCase,就是一个测试用例。 **
什么是测试用例呢?
**就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。 继承自unittest.TestCase的类中,测试方法的名称要以test开头。且只会执行以 test开头定义的方法(测试方法),测试用例执行的顺序会按照方法名的ASCII值排序。 **
**如果想跳过某个测试用例,@unittest.skip(‘描述信息') **
2.2.1运行结果
import unittestclass
Calc_testcase(unittest.TestCase):
def setUp(self) : #测试用例方法执行前的操作
print("start")
def test1(self): #测试用例
resl = c.add(2,3)
self.assertEqual(resl,5)
def tearDown(self) : #测试用例方法执行后的操作
print("end")
if __name__ =="__main__":
unittest.main() #调用测试用例中以test开头的方法
2.3TestSuite
**测试套件,可以将多个测试用例集合在一起,能一起执行选中的测试用例。 **
方式一:
suite = unittest.TestSuite()#创建测试套件
case_list = [“test1”,”test2”….]
For case in case_list:
suite.addTest(类名(case))
方式二
suite = unittest.TestSuite()#创建测试套件
suite.addTest(类名 (“test1“))
suite.addTest(类名 (“test2“))
方式三:(添加一个测试用例类 )
suite = unittest.TestSuite()#创建测试套件
loader = unittest.TestLoader()# 创建一个加载对象
suite .addTest(loader.loadTestsFromTestCase(类名))
2.4TextRunner:执行测试用例
通过TextTestRunner类提供的run()方法来执行test suite/test case
**格式如下: **
runner = unittest.TextTestRunner(verbosity=2)
**runner.run(suite) **
verbosity :表示测试报告信息的详细程度,一共三个值(0,1,2),默认是2
0 (静默模式):
你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
1 (默认模式):
类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
2 (详细模式):
测试结果会显示每个测试用例的所有相关的信息
2.4.1 生成测试报告(重点)
html格式的就是HTMLTestRunner了,HTMLTestRunner是 Python 标准库的 unittest 框架的一个扩展,它可以生成一个直观清晰的 HTML 测试报告。
**使用的前提就是要下载 HTMLTestRunner.py **
**格式如下: **
**with open("../report.html","wb") as f: **
**HTMLTestRunner( **
**stream=f, **
**title="单元测试", **
**description="测试一期", **
**verbosity=2 ).run(suite) **
2.4.2相关参数说明:
stream:指定输出的方式
description:报告中要显示的面熟信息
title:测试报告的标题
verbosity :表示测试报告信息的详细程度,一共三个值,默认是2
0 (静默模式):你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
1 (默认模式):类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
**2 (详细模式):测试结果会显示每个测试用例的所有相关的信息 **
三、断言
assertEqual(a,b):断言a和b是否相等,相等则测试用例通过。
assertNotEqual(a,b):断言a和b是否相等,不相等则测试用例通过。
assertTrue(x):断言x是否True,是True则测试用例通过。
assertFalse(x):断言x是否False,是False则测试用例通过。
assertIs(a,b):断言a是否是b,是则测试用例通过。
assertNotIs(a,b):断言a是否是b,不是则测试用例通过。
assertIsNone(x):断言x是否None,是None则测试用例通过。
assertIsNotNone(x):断言x是否None,不是None则测试用例通过。
assertIn(a,b):断言a是否在b中,在b中则测试用例通过。
assertNotIn(a,b):断言a是否在b中,不在b中则测试用例通过。
assertIsInstance(a,b):断言a是是b的一个实例,是则测试用例通过。
assertNotIsInstance(a,b):断言a是是b的一个实例,不是则测试用例通过。
四、unittest基础操作
1:导入unittest模块 >>>import unittest
2:编写一个类继承unittest.TestCase
3:调用setUp(self), tearDown(self)方法实现测试用例前后阶段的操作
4:编写测试用例方法
(1)该方法必须以test开头,否则在unittest.main()中调用测试找不到该方法
(2)设置断言进行判断,输入数据和输出数据的预期结果
5:创建套件,将多个测试用例存放套件中,一并执行()
6:生成测试报告(python自带或者导入HTMLTestRunner生成html格式的测试报告)
**7:运行测试用例unittest.main(),调用测试用例中以test开头的方法 **
4.1AddDemo.py类
class AddDemoClass():
def add(self,a,b):
c = a+b
print("a+b=%d"%c)
return c
def no_Add(self,a,b):
d = a-b
print("a-b=%d"%d)
return d
a = AddDemoClass()
a.add(1,2)
a.no_Add(1,2)
运行结果:
4.2UnittestAddDemo.py类
import unittestfrom UnittestDemo.AddDemo
import AddDemoClass
class UnittestClass(unittest.TestCase):
# unittest程序开始的方法
setUp()
def setUp(self):
print("start:开始执行")
def test1(self):
a = AddDemoClass()
aa = a.add(4, 5) # aa接收值
print("test1的aa的值为:%d" % aa)
self.assertEqual(aa, 9) # a打印的值是否等于9
@unittest.skip # 跳过test2方法(有时候方法过时需要跳过执行成宿)
def test2(self):
a = AddDemoClass()
bb = a.no_Add(8, 5)
print("test2的bb的值为:%d" % bb)
self.assertEqual(bb, 3)
# unittest程序结束的方法tearDown()
def tearDown(self):
print("end:结束执行")
# 程序入口main方法,不写不调用
if __name__ == '__main__': unittest.main()
** 运行结果:**
4.3UnittestSuite.py类
import unittestfrom UnittestDemo.UnittestAddDemo
import UnittestClass
class UnittestSuiteClass:
def show(self):
suite = unittest.TestSuite()#创建套件
lists = ["test1", "test2"]#注意方法名是否存在
for a in lists:
suite.addTest(UnittestClass(a))
if __name__ == '__main__':
UnittestSuiteClass().show() # 运行添加套件的show()方法
4.4UnittestRunner.py类
import unittestfrom UnittestDemo.UnittestAddDemo import UnittestClass
class UnittestRunnerClass:
def show(self):
suite = unittest.TestSuite() # 创建套件
lists = ["test1", "test2"] # 注意方法名是否存在
for a in lists:
suite.addTest(UnittestClass(a))
print(suite)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
if __name__ == '__main__':
UnittestRunnerClass().show() # 运行添加套件的show()方法
4.5HTMLTestRunner.py类(下方有链接下载)
"""
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.html __author__ = "Wai Yip Tung"__version__ = "0.8.2" """Change HistoryVersion 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 ioimport sysimport timeimport unittestfrom xml.sax import saxutils # ------------------------------------------------------------------------# The redirectors below are used to capture output during testing. Output# sent to sys.stdout and sys.stderr are automatically captured. However# in some cases sys.stdout is already cached before HTMLTestRunner is# invoked (e.g. calling logging.basicConfig). In order to capture those# output, use the redirectors for the cached stream.## e.g.# >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)# >>> class OutputRedirector(object): """ Wrapper to redirect stdout or stderr """ def __init__(self, fp): self.fp = fp def write(self, s): self.fp.write(s) def writelines(self, lines): self.fp.writelines(lines) def flush(self): self.fp.flush() stdout_redirector = OutputRedirector(sys.stdout)stderr_redirector = OutputRedirector(sys.stderr) # ----------------------------------------------------------------------# Template class Template_mixin(object): """ Define a HTML template for report customerization and generation. Overall structure of an HTML report HTML +------------------------+ |<html> | | <head> | | | | STYLESHEET | | +----------------+ | | | | | | +----------------+ | | | | </head> | | | | <body> | | | | HEADING | | +----------------+ | | | | | | +----------------+ | | | | REPORT | | +----------------+ | | | | | | +----------------+ | | | | ENDING | | +----------------+ | | | | | | +----------------+ | | | | </body> | |</html> | +------------------------+ """ STATUS = { 0: 'pass', 1: 'fail', 2: 'error', } DEFAULT_TITLE = 'Unit Test Report' DEFAULT_DESCRIPTION = '' # ------------------------------------------------------------------------ # HTML Template HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head> <title>%(title)s</title> <meta name="generator" content="%(generator)s"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> %(stylesheet)s</head><body><script language="javascript" type="text/javascript"><!--output_list = Array();/* level - 0:Summary; 1:Failed; 2:All */function showCase(level) { trs = document.getElementsByTagName("tr"); for (var i = 0; i < trs.length; i++) { tr = trs[i]; id = tr.id; if (id.substr(0,2) == 'ft') { if (level < 1) { tr.className = 'hiddenRow'; } else { tr.className = ''; } } if (id.substr(0,2) == 'pt') { if (level > 1) { tr.className = ''; } else { tr.className = 'hiddenRow'; } } }}function showClassDetail(cid, count) { var id_list = Array(count); var toHide = 1; for (var i = 0; i < count; i++) { tid0 = 't' + cid.substr(1) + '.' + (i+1); tid = 'f' + tid0; tr = document.getElementById(tid); if (!tr) { tid = 'p' + tid0; tr = document.getElementById(tid); } id_list[i] = tid; if (tr.className) { toHide = 0; } } for (var i = 0; i < count; i++) { tid = id_list[i]; if (toHide) { document.getElementById('div_'+tid).style.display = 'none' document.getElementById(tid).className = 'hiddenRow'; } else { document.getElementById(tid).className = ''; } }}function showTestDetail(div_id){ var details_div = document.getElementById(div_id) var displayState = details_div.style.display // alert(displayState) if (displayState != 'block' ) { displayState = 'block' details_div.style.display = 'block' } else { details_div.style.display = 'none' }}function html_escape(s) { s = s.replace(/&/g,'&'); s = s.replace(/</g,'<'); s = s.replace(/>/g,'>'); return s;}/* obsoleted by detail in <div>function showOutput(id, name) { var w = window.open("", //url name, "resizable,scrollbars,status,width=800,height=450"); d = w.document; d.write("<pre>"); d.write(html_escape(output_list[id])); d.write("\n"); d.write("<a href='javascript:window.close()'>close</a>\n"); d.write("</pre>\n"); d.close();}*/--></script>%(heading)s%(report)s%(ending)s</body></html>""" # variables: (title, generator, stylesheet, heading, report, ending) # ------------------------------------------------------------------------ # Stylesheet # # alternatively use a <link> for external style sheet, e.g. # <link rel="stylesheet" href="$url" type="text/css"> STYLESHEET_TMPL = """<style type="text/css" media="screen">body { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }table { font-size: 100%; }pre { }/* -- heading ---------------------------------------------------------------------- */h1 { font-size: 16pt; color: gray;}.heading { margin-top: 0ex; margin-bottom: 1ex;}.heading .attribute { margin-top: 1ex; margin-bottom: 0;}.heading .description { margin-top: 4ex; margin-bottom: 6ex;}/* -- css div popup ------------------------------------------------------------------------ */a.popup_link {}a.popup_link:hover { color: red;}.popup_window { display: none; position: relative; left: 0px; top: 0px; /*border: solid #627173 1px; */ padding: 10px; background-color: #E6E6D6; font-family: "Lucida Console", "Courier New", Courier, monospace; text-align: left; font-size: 8pt; width: 500px;}}/* -- report ------------------------------------------------------------------------ */#show_detail_line { margin-top: 3ex; margin-bottom: 1ex;}#result_table { width: 80%; border-collapse: collapse; border: 1px solid #777;}#header_row { font-weight: bold; color: white; background-color: #777;}#result_table td { border: 1px solid #777; padding: 2px;}#total_row { font-weight: bold; }.passClass { background-color: #6c6; }.failClass { background-color: #c60; }.errorClass { background-color: #c00; }.passCase { color: #6c6; }.failCase { color: #c60; font-weight: bold; }.errorCase { color: #c00; font-weight: bold; }.hiddenRow { display: none; }.testcase { margin-left: 2em; }/* -- ending ---------------------------------------------------------------------- */#ending {}</style>""" # ------------------------------------------------------------------------ # Heading # HEADING_TMPL = """<div class='heading'><h1>%(title)s</h1>%(parameters)s<p class='description'>%(description)s</p></div>""" # variables: (title, parameters, description) HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>""" # variables: (name, value) # ------------------------------------------------------------------------ # Report # REPORT_TMPL = """<p id='show_detail_line'>Show<a href='javascript:showCase(0)'>Summary</a><a href='javascript:showCase(1)'>Failed</a><a href='javascript:showCase(2)'>All</a></p><table id='result_table'><colgroup><col align='left' /><col align='right' /><col align='right' /><col align='right' /><col align='right' /><col align='right' /></colgroup><tr id='header_row'> <td>Test Group/Test case</td> <td>Count</td> <td>Pass</td> <td>Fail</td> <td>Error</td> <td>View</td></tr>%(test_list)s<tr id='total_row'> <td>Total</td> <td>%(count)s</td> <td>%(Pass)s</td> <td>%(fail)s</td> <td>%(error)s</td> <td> </td></tr></table>""" # variables: (test_list, count, Pass, fail, error) REPORT_CLASS_TMPL = r"""<tr class='%(style)s'> <td>%(desc)s</td> <td>%(count)s</td> <td>%(Pass)s</td> <td>%(fail)s</td> <td>%(error)s</td> <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td></tr>""" # variables: (style, desc, count, Pass, fail, error, cid) REPORT_TEST_WITH_OUTPUT_TMPL = r"""<tr id='%(tid)s' class='%(Class)s'> <td class='%(style)s'><div class='testcase'>%(desc)s</div></td> <td colspan='5' align='center'> <!--css div popup start--> <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" > %(status)s</a> <div id='div_%(tid)s' class="popup_window"> <div style='text-align: right; color:red;cursor:pointer'> <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " > [x]</a> </div> <pre> %(script)s </pre> </div> <!--css div popup end--> </td></tr>""" # variables: (tid, Class, style, desc, status) REPORT_TEST_NO_OUTPUT_TMPL = r"""<tr id='%(tid)s' class='%(Class)s'> <td class='%(style)s'><div class='testcase'>%(desc)s</div></td> <td colspan='5' align='center'>%(status)s</td></tr>""" # variables: (tid, Class, style, desc, status) REPORT_TEST_OUTPUT_TMPL = r"""%(id)s: %(output)s""" # variables: (id, output) # ------------------------------------------------------------------------ # ENDING # ENDING_TMPL = """<div id='ending'> </div>""" # -------------------- The end of the Template class ------------------- TestResult = unittest.TestResult class _TestResult(TestResult): # note: _TestResult is a pure representation of results. # It lacks the output and reporting ability compares to unittest._TextTestResult. def __init__(self, verbosity=1): TestResult.__init__(self) self.stdout0 = None self.stderr0 = None self.success_count = 0 self.failure_count = 0 self.error_count = 0 self.verbosity = verbosity # result is a list of result in 4 tuple # ( # result code (0: success; 1: fail; 2: error), # TestCase object, # Test output (byte string), # stack trace, # ) self.result = [] def startTest(self, test): TestResult.startTest(self, test) # just one buffer for both stdout and stderr self.outputBuffer = 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): 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') class HTMLTestRunner(Template_mixin): """ """ def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): self.stream = stream self.verbosity = verbosity if title is None: self.title = self.DEFAULT_TITLE else: self.title = title if description is None: self.description = self.DEFAULT_DESCRIPTION else: self.description = description self.startTime = datetime.datetime.now() def run(self, test): "Run the given test case or test suite." result = _TestResult(self.verbosity) test(result) self.stopTime = datetime.datetime.now() self.generateReport(test, result) print(sys.stderr, '\nTimeElapsed: %s' % (self.stopTime-self.startTime)) return result def sortResult(self, result_list): # unittest does not seems to run in any particular order. # Here at least we want to group them together by class. rmap = {} classes = [] for n,t,o,e in result_list: cls = t.__class__ if not cls 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('Pass %s' % result.success_count) if result.failure_count: status.append('Failure %s' % result.failure_count) if result.error_count: status.append('Error %s' % result.error_count ) if status: status = ' '.join(status) else: status = 'none' return [ ('Start Time', startTime), ('Duration', duration), ('Status', status), ] def generateReport(self, test, result): report_attrs = self.getReportAttributes(result) generator = 'HTMLTestRunner %s' % __version__ stylesheet = self._generate_stylesheet() heading = self._generate_heading(report_attrs) report = self._generate_report(result) ending = self._generate_ending() output = self.HTML_TMPL % dict( title = saxutils.escape(self.title), generator = generator, stylesheet = stylesheet, heading = heading, report = report, ending = ending, ) self.stream.write(output.encode('utf8')) def _generate_stylesheet(self): return self.STYLESHEET_TMPL def _generate_heading(self, report_attrs): a_lines = [] for name, value in report_attrs: line = self.HEADING_ATTRIBUTE_TMPL % dict( name = saxutils.escape(name), value = saxutils.escape(value), ) a_lines.append(line) heading = self.HEADING_TMPL % dict( title = saxutils.escape(self.title), parameters = ''.join(a_lines), description = saxutils.escape(self.description), ) return heading def _generate_report(self, result): rows = [] sortedResult = self.sortResult(result.result) for cid, (cls, cls_results) in enumerate(sortedResult): # subtotal for a class np = nf = ne = 0 for n,t,o,e in cls_results: if n == 0: np += 1 elif n == 1: nf += 1 else: ne += 1 # format class description if cls.__module__ == "__main__": name = cls.__name__ else: name = "%s.%s" % (cls.__module__, cls.__name__) doc = cls.__doc__ and cls.__doc__.split("\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_report_test(self, rows, cid, tid, n, t, o, e): # e.g. 'pt1.1', 'ft1.1', etc has_output = bool(o or e) tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) name = t.id().split('.')[-1] doc = t.shortDescription() or "" desc = doc and ('%s: %s' % (name, doc)) or name tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL # o and e should be byte string because they are collected from stdout and stderr? if isinstance(o,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # uo = unicode(o.encode('string_escape')) uo = e else: uo = o if isinstance(e,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # ue = unicode(e.encode('string_escape')) ue = e else: ue = e script = self.REPORT_TEST_OUTPUT_TMPL % dict( id = tid, output = saxutils.escape(uo+ue), ) row = tmpl % dict( tid = tid, Class = (n == 0 and 'hiddenRow' or 'none'), style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), desc = desc, script = script, status = self.STATUS[n], ) rows.append(row) if not has_output: return def _generate_ending(self): return self.ENDING_TMPL ############################################################################### Facilities for running tests from the command line############################################################################## # Note: Reuse unittest.TestProgram to launch test. In the future we may# build our own launcher to support more specific command line# parameters like test title, CSS, etc.class TestProgram(unittest.TestProgram): """ A variation of the unittest.TestProgram. Please refer to the base class for command line parameters. """ def runTests(self): # Pick HTMLTestRunner as the default test runner. # base class's testRunner parameter is not useful because it means # we have to instantiate HTMLTestRunner before we know self.verbosity. if self.testRunner is None: self.testRunner = HTMLTestRunner(verbosity=self.verbosity) unittest.TestProgram.runTests(self) main = TestProgram ############################################################################### Executing this module from the command line############################################################################## if __name__ == "__main__": main(module=None)
4.6创建demo.html文件
4.7HtmlTestRunnerDemo.py类
import unittestfrom UnittestDemo.UnittestAddDemo
import UnittestClassfrom CommenDemo.HTMLTestRunner import HTMLTestRunner
class HtmlTestRunnerClass:
def show(self):
suite = unittest.TestSuite() # 创建套件
lists = ["test1", "test2"] # 注意方法名是否存在
for a in lists:
suite.addTest(UnittestClass(a)) # print(suite)
with open("../demo.html", "wb") as f:
HTMLTestRunner(
stream=f,
verbosity=2,
title="我是标题AAAAAAAAAAAAAAAAAAAAAAAAAAAAA", description="描述展示HHHHHHHHHHHHHHHHHHHHHHH" ).run(suite)
if __name__ == '__main__':
HtmlTestRunnerClass().show() # 运行添加套件的show()方法
运行结果:
4.8生成测试报告
五、 *******************HTMLTestRunner.py文件分享********************
网盘不让发否则会锁定需要私我,网上也很多可以百科