unittest单元测试学习之 unittest单元测试的介绍以及常见操作(断言)

目录

一、单元测试,集成测试,功能测试

二、Unittest重要组成

2.1TestFixture

2.2TestCase:测试用例

2.2.1运行结果

2.3TestSuite

2.4TextRunner:执行测试用例

2.4.1 生成测试报告(重点)

2.4.2相关参数说明:

三、断言

四、unittest基础操作

4.1AddDemo.py类

4.2UnittestAddDemo.py类

4.3UnittestSuite.py类

4.4UnittestRunner.py类

4.5HTMLTestRunner.py类(下方有链接下载)

4.6创建demo.html文件

4.7HtmlTestRunnerDemo.py类

4.8生成测试报告

五、 *******************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) **

image

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)

运行结果:

image

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()

** 运行结果:**

image

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()方法
image

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,'&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>%(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>&nbsp;</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'>&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 = []      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文件

image

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()方法

运行结果:

image
image
image
image

4.8生成测试报告

image

五、 *******************HTMLTestRunner.py文件分享********************

网盘不让发否则会锁定需要私我,网上也很多可以百科

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341