单元测试框架可以完成以下三件事情:
- 提供用例组织与执行:当测试用例只有几条时,可以不用考虑用例的组织,但是当测试用例达到成千上百条,大量的测试用例堆积在一起,就产生了扩展性与维护性等问题,需要考虑用例的规范和组织问题了,单元测试框架就是用来解决这个问题的
- 提供丰富的比较方法:不论是功能测试,还是单元测试,在用例执行完成之后需要将实际结果与预期结果进行比较(断言),从而断定用例是否执行通过。单元测试框架一般会提供丰富的断言方法,比如,判断相等/不相等、包含/不包含、true/false的断言方法。
- 提供丰富的日志:当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后,能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等
一般的单元测试框架都会提供这些功能,从单元测试框架的这些特性来看,它同样适用于web自动化用例的开发与执行。
认识unittest
单元测试负责对最小的软件设计单元(模块)进行验证,它使用软件设计文档中对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误。
在Python语言下有诸多单元测试框架,如doctest、unittest、pytest、nose等,unittest框架(原名PyUnit)为Python语言自带的单元测试框架
认识单元测试
单元测试本身就是通过一段代码验证另一段代码,所以不用单元测试框架也可以写单元测试。
首先创建一个被测试类count.py
#计算器类
from selenium import webdriver
class Count:
def __init__(self,a,b):
self.a = int(a)
self.b = int(b)
def add(self):
return self.a+self.b
再创建一个测试类test.py
from count import Count
#测试两个数相加
class TestCount:
def test_add(self):
try:
j=Count(2,4)
add = j.add()
assert (add==6)
except AssertionError:
print('Integer addition result error!')
else:
print("test pass!")
#执行测试类的测试方法
if __name__ == '__main__':
mytest=TestCount()
mytest.test_add()
运行结果如下:
将test中的(add6)改为(add7)
运行结果如下:
以上代码能看得出,需要些的测试代码很多,运行代码很麻烦,而且代码很凌乱,表示不适用单元测试框架进行单元测试成本很高,比较难维护,而且代码不统一,不规范
写一个单元测试框架的测试用例test01.py:
#引入单元测试框架
import unittest
from count import Count
class TestCount(unittest.TestCase):
#这里的定义必须以test开头,执行的时候,才会认为这个是个需要测试的方法
def test_add(self):
t = Count(2,4)
add = t.add()
#写个断言
self.assertEqual(add,6)
#执行测试类的测试方法
if __name__ == '__main__':
unittest.main()
运行如下结果:
将test01中的self.assertEqual(add,6)改为self.assertEqual(add,7),运行结果如下:
重要概念
- Test Case
一个Test Case的实例就是一个测试用例,,一个测试用例就是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证。一个完整的测试流程包括测试前准备环境的搭建(setUp),实现测试过程的代码(run),以及测试后环境的还原(teardown) - Test Suite
一个功能的验证往往是需要多个测试用例的,可以把多个测试用例集合在一起执行,这就产生了测试套件 Test Suite概念, Test Suite是用来组装单个测试用例。可以通过addtest加载testcase到testsuite中,从而返回一个testsuite实例 - Test Runner
测试的执行也是单元测试中的非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果,unittest单元测试框架中,通过TextTestRunner类所提供的run()方法来执行testsuit、testcase。test runner可以使用图形界面,文本界面,或返回一个特殊的值等方式来表示测试执行的结果 - Test Fixture
对于一个测试用例环境的搭建和销毁,就是一个fixture,通过覆盖testcase的setup()和teardown()方法来实现。例如在这个测试用例中需要访问数据库,那么可以在setup()中通过建立数据库连接来进行初始化,在teardown()中清除在数据库中产生的数据,然后关闭连接等。注意teardown的过程很重要,要为下一个testcase留下一个干净的环境。
通过以上概念,我们将test01.py丰富以下:
#丰富单元测试的执行过程
#引入单元测试框架
import unittest
from count import Count
class TestCount(unittest.TestCase):
def setUp(self): #针对每个测试用例之前,需要执行
print("star test")
#这里的定义必须以test开头,执行的时候,才会认为这个是个需要测试的方法
def test_add(self):
t = Count(2,4)
add = t.add()
#写个断言
self.assertEqual(add,6)
print("test case1")
def test_add1(self):#第二条测试用例
t = Count(2,5)
add = t.add()
#写个断言
self.assertEqual(add,7)
print("test case2")
def tearDown(self): #针对每个测试用例之后需要执行的
print("test end")
#执行测试类的测试方法
if __name__ == '__main__':
# 构造测试集
suite = unittest.TestSuite
suite.addTest(TestCount("test_add")) #如果不是当前文件下的信息,就写suite.addTest(文件名.类("方法"))
suite.addTest(TestCount("test_add1"))
#执行测试
runner = unittest.TextTestRunner()
runner.run(suite)
运行结果如下:
断言的方法合集:
当断言抛出的错误信息是可以自定义的: