unittest、HTMLTestRunner - 使用

一、单元测试 - unittest 

Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作。

在说unittest之前,先说几个概念:

TestCase 也就是测试用例

TestSuite 多个测试用例集合在一起,就是TestSuite

TestLoader是用来加载TestCase到TestSuite中的

TestRunner是来执行测试用例的,测试的结果会保存到TestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息

下面写一个简单的单元测试用例

https://pymotw.com/3/unittest/index.html
https://www.cnblogs.com/feng0815/p/8045850.html
https://pypi.org/project/html-testRunner/#description
https://github.com/oldani/HtmlTestRunner
--------------------------------

import unittest

class MyTest(unittest.TestCase):  # 继承unittest.TestCase
    def tearDown(self):
        # 每个测试用例执行之后做操作
        print('111')

    def setUp(self):
        # 每个测试用例执行之前做操作
        print('22222')

    @classmethod
    def tearDownClass(self):
    # 必须使用 @ classmethod装饰器, 所有test运行完后运行一次
         print('4444444')
    @classmethod
    def setUpClass(self):
    # 必须使用@classmethod 装饰器,所有test运行前运行一次
        print('33333')

    def test_a_run(self):
        self.assertEqual(1, 1)  # 测试用例
        
    def test_b_run(self):
        self.assertEqual(2, 2)  # 测试用例
        
if __name__ == '__main__':
    unittest.main()#运行所有的测试用例

下面是一些常用的断言,也就是校验结果

复制代码
        assertEqual(a, b)     a == b      
        assertNotEqual(a, b)     a != b      
        assertTrue(x)     bool(x) is True      
        assertFalse(x)     bool(x) is False      
        assertIsNone(x)     x is None     
        assertIsNotNone(x)     x is not None   
        assertIn(a, b)     a in b    
        assertNotIn(a, b)     a not in b
复制代码

那如何生成一个测试报告呢,需要加入另外一个模块了,HTMLTestRunner,这个模块需要自己安装,使用执行测试用例就会生成一个html的测试报告,里面会有每个测试用例的执行结果,代码如下:

复制代码
        import HTMLTestRunner        
        import unittest
        class MyTest(unittest.TestCase):#继承unittest.TestCase
            def tearDown(self):
                #每个测试用例执行之后做操作
                print('111')
            def setUp(self):
                #每个测试用例执行之前做操作
                print(22222)
            def test_run(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
            def test_run2(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
            def test_run3(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
            def test_run1(self):
                # self.assertEqual(1,1)
                self.assertIs(1,1)
                #测试用例
        if __name__ == '__main__':
            test_suite = unittest.TestSuite()#创建一个测试集合
            test_suite.addTest(MyTest('test_run1'))#测试套件中添加测试用例
            #test_suite.addTest(unittest.makeSuite(MyTest))#使用makeSuite方法添加所有的测试方法
            fp = open('res.html','wb')#打开一个保存结果的html文件
            runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title='api测试报告',description='测试情况')
            #生成执行用例的对象
            runner.run(test_suite)
            #执行测试套件
复制代码

如果我们有很多个模块,每个模块下面都写了很多python文件,每个python文件里面都有测试用例,那怎么把这个目录下的用例都执行了呢,就要先找到这个目录下的所有python文件,然后找到里面的测试用例,逐个执行,代码如下:

复制代码
        import unittest,HTMLTestRunner
        suite = unittest.TestSuite()#创建测试套件
        all_cases = unittest.defaultTestLoader.discover('.','test_*.py')
        #找到某个目录下所有的以test开头的Python文件里面的测试用例
        for case in all_cases:
            suite.addTests(case)#把所有的测试用例添加进来
        fp = open('res.html','wb')
        runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title='all_tests',description='所有测试情况')
        runner.run(suite)
        #运行测试
复制代码

我们在后续进行持续集成的时候,要让代码自动运行,就会用到Jenkins了,但是上面产生的测试报告都是html格式的,Jenkins不认识,就在Jenkins里面显示不出来。那咱们就要产生一些Jenkins认识的测试报告,Jenkins认识xml格式的报告,那咱们就产生xml格式的呗,就需要用一个新的模块,xmlrunner,安装直接 pip install xmlrunner即可,代码如下:

复制代码
import unittest
import xmlrunner
#导入这个模块
class My(unittest.TestCase):
 
    def test1(self,a,b,c):
        self.assertEqual(a+b,c)
 
if __name__=='__main__':
    test_suite = unittest.TestSuite()
    test_suite.addTest(unittest.makeSuite(My))
    runner = xmlrunner.XMLTestRunner(output='report')#指定报告放的目录
    runner.run(test_suite)
复制代码

然后咱们运行,可以看到在report目录下已经产生了xml格式的报告了,而且还自动把日期加上了

 

二、py2 - HTMLTestRunner 的安装与使用

HTMLTestRunner 是 Python 标准库的 unittest 模块的一个扩展。

在使用之前,我们需要先安装他。下载地址:https://pypi.python.org/pypi/HTMLTestRunner。放在C:\Python27\Lib该目录

接下来需要找到python的ide,敲入“import HTMLTestRunner”,没报错就代表我们导入HTMLTestRunner模块成功。如下图

 

示例:

# -*- coding:utf-8 -*-
"""
https://www.cnblogs.com/8013-cmf/p/6644366.html
http://tungwaiyip.info/software/HTMLTestRunner.html

"""
import HTMLTestRunner
import unittest

class testadd(unittest.TestCase):
    def setUp(self):
        print('111')
    def test_add1(self):
        self.assertEqual(2+3,10)
    def test_add2(self):
        self.assertEqual(0+8+7,15)
    def tearDown(self):
        print('222')

def suite():
    suiteTest=unittest.TestSuite()
    suiteTest.addTest(testadd("test_add1"))
    suiteTest.addTest(testadd("test_add2"))
    return suiteTest

if __name__=="__main__":
    filepath='D:\\pyresult.html'
    fp=file(filepath,'wb')
    #定义测试报告的标题与描述
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'我是测试报告的标题',description=u'我是测试报告的描述')
    runner.run(suite())
    fp.close()

结果:

 

疑问:

1.为什么我的代码运行成功了,但测试报告没有生成?

  对于我这种刚学习的小白来说,这个问题真是很纠结。

  因为我一直是快捷键运行代码,用unittest来启动,就没执行自己的main,执行的是unittest的main。详情请参照:http://blog.csdn.net/xie_0723/article/details/50825310

  解决方法:Run-Run-运行代码。或者alt+shift+f10-运行代码

2.上面所说的2中运行方式有什么不同呢?

  • 第一种,直接运行,执行的是unittest的man
  • 第二种,就从自己的main入口进入执行的
  • 更多详情请参照:http://www.ithao123.cn/content-6035445.html

3.我测试报告也正常生成了,但打开html的测试报告一片空白,这是为什么?

  因为你没关闭文件。

  解决方案:fp.close()

 

三、py3 - HTMLTestRunner 的安装与使用

http://www.cnblogs.com/miniren/p/5301081.html#commentform (这里很详细)

HTMLTestRunner

由于 HTMLTestRunner.py 原本就是python2版本,目前还没找到python3版本,所以需要我们自己修改 HTMLTestRunner.py 文件。

1. 修改的地方

问题一:No module named StringIO

原因:python 3 中 没有 StringIO 这个模块。这里我们需要使用io 这个模块来代替。

解决方法:

第94行引入的名称要改,从 import StringIO 改成import io。

相应的,539行 self.outputBuffer = StringIO.StringIO() 要改成self.outputBuffer = io.BytesIO()


问题二:AttributeError: 'dict' object has no attribute 'has_key'

原因:python 3 字典类型的object 已经不支持 has_key函数,我们需要使用in 来进行遍历。

解决方法:

定位到642行,if not rmap.has_key(cls): 需要换成 if not cls in rmap:


问题三:'str' object has no attribute 'decode'

原因:python3 里面对字符的操作中,decode已经拿掉了。

解决方法:

定位到772行,把 ue = e.decode('latin-1') 直接改成 ue = e 。

另外766还有类似的uo = o.decode('latin-1'),改成 uo=o ;


问题四 :TypeError: can't concat bytes to str

原因:定位一下,报在了778行的内容escape(uo+ue) 。这是因为我们上面给uo赋值的时候,走的是else流程,uo被赋值的是bytes类型的值。 而bytes类型不能直接转化为str类型。所以我们需要在前面给uo赋值的时候先将bytes类型转换为 str类型。

解决方法:

修改768行的 uo = o ,直接改成 uo = o.decode('utf-8') 。

另外 774还有类似的  ue = e, 改成 ue = e.decode('utf-8')。

 

问题五:TypeError: unsupported operand type(s) for >>: 'builtin_function_or_method' and 'RPCProxy'

原因: python3  不支持 print >> sys.stderr 这种写法,这里定义输出流的话,采用print("This is print str",file=sys.stderr) 这种方式。

解决方法:

定位到631行,把print的语句修改掉,原来是print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime), 可改成 print('\nTime Elapsed: %s' % (self.stopTime-self.startTime),file=sys.stderr)

 

问题六:TypeError: 'str' does not support the buffer interface

原因:定位一下,问题出在118行,这里s是str类型,我们需要把传过来的s转化为bytes类型。

解决方法:

定位到118行,把 self.fp.write(s) 修改为 self.fp.write(bytes(s,'UTF-8')) 即可。

2. 保存

修改后对HTMLTestRunner.py 保存一下。

3. 调用语句更改

python3 里面打开文件使用 open,不要再去用file了。

即 fp = file(filename,'wb')替换成 fp = open(filename,'wb');

关闭该文件可用fp.close()

 

备注: 改动之后,中文也不会乱码。

 

# -*- coding:utf-8 -*-
"""
https://www.cnblogs.com/wm1012/p/6554344.html
http://www.cnblogs.com/miniren/p/5301081.html#commentform
"""
import HTMLTestRunner
import unittest


class MyTest(unittest.TestCase):  # 继承unittest.TestCase
    def tearDown(self):
        # 每个测试用例执行之后做操作
        print('111')

    def setUp(self):
        # 每个测试用例执行之前做操作
        print(22222)

    def test_run(self):
        # self.assertEqual(1,1)
        self.assertIs(1, 1)
        # 测试用例

    def test_run2(self):
        # self.assertEqual(1,1)
        self.assertIs(1, 1)
        # 测试用例

    def test_run3(self):
        # self.assertEqual(1,1)
        self.assertIs(1, 1)
        # 测试用例

    def test_run1(self):
        self.assertEqual(1,1)
        # self.assertIs(2, 1)
        # 测试用例


if __name__ == '__main__':
    test_suite = unittest.TestSuite()  # 创建一个测试集合
    # test_suite.addTest(MyTest('test_run1'))  # 测试套件中添加测试用例
    test_suite.addTest(unittest.makeSuite(MyTest))#使用makeSuite方法添加所有的测试方法
    fp = open('res.html', 'wb')  # 打开一个保存结果的html文件
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='api测试报告', description='测试情况')
    # 生成执行用例的对象
    runner.run(test_suite)
    # 执行测试套件
    fp.close()
    

 

四、HTMLTestRunner - 补充

下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html

使用时,先建立一个”PyDev Package“,将下载下来的HTMLTestRunner.py文件拷贝在该目录下。

(py2) - 例子:testcase5_dynamic.py

复制代码
import unittest
from dev.widget import Widget

class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget=Widget()
        
    def tearDown(self):
        self.widget.dispose()
        self.widget=None
        
    def testSize(self):
        self.assertEqual(self.widget.getSize(), (40,40), "Wrong")
        
    def testResize(self):
        self.widget.resize(100, 100)
        self.assertEqual(self.widget.getSize(), (100,100), "Wrong")
复制代码

html_report.py:

复制代码
#coding:utf-8

from lib import HTMLTestRunner
import unittest
from testcase5_dynamic import WidgetTestCase

if __name__=='__main__':
    suite=unittest.makeSuite(WidgetTestCase)
    filename='D:\\myreport.html'
    fp=file(filename,'wb')
    runner=HTMLTestRunner.HTMLTestRunner(fp,title=u'my unit test',description=u'This is a report test')
    runner.run(suite)
复制代码

Run的时候,需要使用Python Run,使用Python unit-test跑测试生成不了myreport.html,目前还不知道为什么。

有时候,不会立即生成D:\\myreport.html,我们可以自己先建立一个空的myreport.html,这样再运行之后打开就会看到报告内容。

 

 

1. 问题: 执行测试用例的过程中,不会打印任何东西,导致上个厕所或第二天回来时,根本不知道执行到哪了,或者执行多少测试用例了。

解决思路: 每次执行一个测试用例时,就打印该测试用例的名称。

解决方案: 调用HTMLTestRunner时,定义 verbosity 为大于1的整数,比如说 2:

runner=HTMLTestRunner.HTMLTestRunner(fp,title="xxxx",description="xxx",verbosity=2)

在控制台console 就可以看到每执行一条用例,就会有如下输出:

E  test (testcases.login.testcase1.MyTest)
ok  test (testcases.login.testcase2.MyTest)
F  test (testcases.login.testcase3.MyTest)

2. HTMLTestRunner 脚本阅读。

复制代码
class OutputRedirector(object)
# 将输出进行重定向

class Template_mixin(object)
# 定义生成HTML结果文件所需要的模板。
# 如果我们想改变HTML的格式等待,可以在这里进行改动

class _TestResult(TestResult)
# 定义继承自 unittest.TestResult 的 类。
# 这里重写了 unittest.TestResult 的多个方法,比如 startTest(self, test) 等等

class HTMLTestRunner(Template_mixin)
# 这里可以说是使用 HTMLTestRunner.py 的入口。定义了多个我们可以看到的方法,比如 run(self, test)

class TestProgram(unittest.TestProgram)
# 这里继承自 unittest.TestProgram 类,重写了 runTests 方法。
# 用于命令行执行测试
复制代码

3. 缺点:使用HTMLTestRunner的执行测试用例的过程中,如果中间中断执行,则已经执行完的用例结果也不会打印到html文件。

目前我这边是自己编写脚本生成html来代替使用 HTMLTestRunner ,感觉 HTMLTestRunner 应该没有每执行一条用例就讲结果写入到html结果文件的方法。

posted @ 2018-08-11 18:55  Alice的小屋  阅读(1577)  评论(0)    收藏  举报