介绍
UnitTest是Python自带的测试框架,主要用于单元测试~说到UnitTest需要了解几个概念:TestCase(测试用例),TestFixture(测试固件),TestSuite(测试套件),TestRunner(测试运行器)。接下来分别学习一下:
首先,我们写一个简单的计算器类,其中有加减乘除四个方法,我们将针对这个计算器类进行单元测试。
#coding:utf-8
class MyCalculator():
def __init__(self):
pass
#加
def add(self,data1,data2):
return data1+data2
#减
def minus(self,data1,data2):
return data1-data2
#乘
def multiply(self,data1,data2):
return data1*data2
#除
def divide(self,data1,data2):
return data1/data2
测试用例与测试固件:
新建一个测试类,继承unittest.TestCase,在该类中写的每一个函数均可理解为一条测试用例。正常来讲,每个测试用例的内容就是每个测试点。如下代码:
# -*- coding:utf-8 -*-
from firstpackage.MyCalculator import MyCalculator
import unittest
import HTMLTestRunner
class TestCalculator(unittest.TestCase):
def setUp(self):
self.myCalculator=MyCalculator()
print('所有用例执行之前都会执行这里!')
def tearDown(self):
print('所有用例执行之后都会执行这里!')
def test_add(self):
result=self.myCalculator.add(1,2)
self.assertEqual(result,3)
print('执行了test_add测试用例')
def test_minus(self):
result=self.myCalculator.minus(3,2)
self.assertEqual(result,1)
print('执行了test_minus测试用例')
def test_multiply(self):
result=self.myCalculator.multiply(2,2)
self.assertEqual(result,4)
print('执行了test_multiply测试用例')
def test_divide(self):
result=self.myCalculator.divide(6,2)
self.assertEqual(result,3)
print('执行了test_divide测试用例')
上面代码就是针对于我们的计算器编写的单元测试代码,我们先导入了需要的模块,其中unittest模块是python自带的,所以直接导入即可;随后新建了一个类TestCalculator,然后又继承了unittest.TestCase,此时TestCalculator就变成了测试实例;其中的test_add,test_minus,test_multiply,test_divide分别是四个测试用例,每个测试用例均是调用了计算器中的功能,得到返回结果后,又与预期结果进行断言,如果断言成功则该用例执行成功,如果断言失败,则该用例执行失败。说到这,说一下unittest的断言函数,如下表
方法 | 含义 |
---|---|
assertEqual(a, b) | a ==b |
assertNotEqual(a, b) | a !=b |
assertTrue(x) | bool(x) is True |
assertFalse(x) | Bool(x) is False |
assertIs(a, b) | a is b |
assertIsNot(a, b) | a is not b |
assertIsNone(x) | x is None |
assertIsNotNone(x) | x is not None |
assertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
assertIsInstance(a, b) | isinstance(a,b) |
assertNotIsInstance(a, b) | not isinstance(a,b) |
你会发现上面代码中多了两个方法setUp,teardown。他们就是我们要说的测试固件,什么是测试固件?简单来说就是测试固有的部分,也就是各个测试用例相同的部分。setup代表起始,teardown代表结束,这两个都是unittest.TestCase中的函数,我们是将它方法重写而已。这两个函数的代码会在每个测试用例执行的前后各执行一次。在例子中,初始化Calculate对象是各个用例的共同部分,便写在了setup中;如果没有setup,我们就需要在每个测试用例都进行初始化,比较麻烦。
除了setup和teardown,还需要了解两个固件:setUpClass和tearDownClass,这两个方法中的代码会在所有的用例执行前后各执行一次,注意,是所有的用例执行前后各执行一次,不是每个用例执行前后哦。还需要注意的是这两个方法都需要classmethod装饰器。
@classmethod
def setUpClass(cls):
print("整个测试之前执行这里")
@classmethod
def tearDownClass(cls):
print("整个测试之后执行这里")
测试套件
什么是测试套件?测试套件就是测试用例的集合。我们可以根据需求选取各个测试用例组成测试套件,然后运行该套件,便会执行套件中的各个用例。
有两种方法进行组合测试套件,第一种方法如下代码:这是一个返回测试套件的方法,先是初始化了unittest.TestSuite对象,随后建立了用例的列表,最后调用addTests()将用例列表添加到测试条件中,并将套件返回。
def makeSuite1():
mySuite=unittest.TestSuite()
myTestCases=[TestCalculator('test_add'),TestCalculator('test_minus'),
TestCalculator('test_divide'),TestCalculator('test_multiply')]
mySuite.addTests(myTestCases)
return mySuite
PS:你也可以调用addTest()进行一个测试用例的添加:
mySuite.addTest(TestCalculator('test_add'))
另一种建立测试套件的方法是调用unittest.makeSuite(),如下代码:是将TestCalculator(类)中test开头的测试用例(函数)组合测试套件,并返回。
def makeSuite2():
mySuite=unittest.makeSuite(TestCalculator,'test')
return mySuite
那么这两种方法有什么区别呢?显而易见,第一种方法灵活,第二中方法方便;除此之外,第一种方法的用例执行顺序便是你添加的顺序,而二种方法的执行顺序是按照函数名除test之外的第一个字母在字母表中的顺序。
测试运行器
测试运行器,顾名思义,便是用来执行测试用例(套件)的。最常见的是TextTestRunner,它可以执行测试并将结果打印出来。我们先是初始化unittest.TextTestRunner对象,随用调用它的run()即可,传入的参数便是我们组成的测试套件。
if __name__=="__main__":
runner=unittest.TextTestRunner()
runner.run(makeSuite1())
运行结果如图:
是不是感觉测试结果不是很直观?接下来介绍一下HTMLTESTRunner,这是一个第三方模块,可以将运行结果形成html形式。需要注意的是该模块不能通过pip下载,需要在网上下载后放到python的安装目录lib文件夹下。该模块是基于python2开发的,如果在python3下使用,需要修改源码,这里不多赘述,可以参考https://www.cnblogs.com/miniren/p/5301081.html(感谢该作者)。
if __name__=="__main__":
file=open("report.html","wb")
runner=HTMLTestRunner.HTMLTestRunner(stream=file,title="测试报告",
description="网页版的unittest测试报告")
runner.run(makeSuite1())
上面的代码便是用HTMLTestRunner运行测试,先建立了一个html文件用于保存运行结果,随后初始化了HTMLTestRunner对象,最后调用run()执行测试。运行后会形成一个html文件,打开如图:
UnitTest用起来是不是很简单呢?