一、什么是unittest
unittest是Python内置标准库、主要用于单元测试,类似于JUnit框架。
二、测试组件化
unittest 中内置有5个组件、我们通常分别叫它们为:TestCase(测试用例)、TestSuite(测试用例套件)、TestFixture(测试固件)、TestLoader(测试加载器)、TestRunner(测试执行器)
在学习unittest之前我们一定要知道unittest的五大组件概念
那么我们就来看看测试组件化的具体内容吧!
TestCase:
一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码 (run),以及测试后环境的还原(tearDown)。元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。
TestSuite:
多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite、用于组织管理所有的测试用例。
TestRunner:
是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。
TestLoader:
是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例。
TestFixture:
对一个测试用例环境的搭建和销毁,是一个fixture,通过覆盖 TestCase的setUp()和tearDown()方法来实现。这个有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在setUp() 中建立数据库连接以及进行一些初始化,在tearDown()中清除在数据库中产生的数据,然后关闭连接。注意tearDown的过程很重要,要为以后的 TestCase留下一个干净的环境。关于fixture,还有一个专门的库函数叫做fixtures,功能更加强大。
使用unittest编写python的单元测试代码,包括如下几个步骤:
1、编写一个python类,继承 unittest模块中的TestCase类,这就是一个测试类
2、在上面编写的测试类中定义测试方法(这个就是指的测试用例),每个方法的方法名要求以 test 打头,没有额外的参数。在该测试方法中 调用被测试代码,校验测试结果,TestCase类中提供了很多标准的校验方法,如 最常见的assertEqual。
3、执行 unittest.main() ,该函数会负责运行测试,它会实例化所有TestCase的子类,并运行其中所有以test打头的方法。
基本工作原理
1、首先需要定义一个测试类、并进行继承至unittest.TestCase类
2、再需要进行定义带有test_开头的方法、该方法则被当做为一个测试用例
3、定义完测试用例后在运行时会由TestLoader类进行加载定义好的测试类、并进行从中查找带有test_开头的方法,并且将带有test_开头的方法进行加载到TestSuite类中集中管理
4、然后再由TestRunner类中的run()进行执行TestSuite中的所有测试用例集
5、最后再将TestRunner执行完后的测试用例后再将结果进行传递给TestResult类进行输出测试结果
6、以上所有的操作都集成到了unittest.main()方法中进行处理
TestCase类
是定义测试用例的基类、在进行定义一个测试用例时都需要进行集成至该类;
测试固件
TestCase类中有四个测试固件方法分别是:
我们可以看到unittest源码中定义的四个方法
这里是部分核心代码源码
class TestCase(object):
failureException = AssertionError
longMessage = True
maxDiff = 80*8
# 如果一个字符串比_diffThreshold长,则使用普通比较
# of difflib. See #11763.
_diffThreshold = 2**16
# TestSuite用于类设置的属性
_classSetupFailed = False
_class_cleanups = []
def __init__(self, methodName='runTest'):
"""
"""
self._testMethodName = methodName
self._outcome = None
# 获取的是test方法中的描述内容
self._testMethodDoc = 'No test'
try:
testMethod = getattr(self, methodName)
except AttributeError:
if methodName != 'runTest':
# we allow instantiation with no explicit method name
# but not an *incorrect* or missing method name
raise ValueError("no such test method in %s: %s" %
(self.__class__, methodName))
else:
self._testMethodDoc = testMethod.__doc__
self._cleanups = []
self._subtest = None
.......
# 四个测试固件方法
def setUp(self):
"在每次测试用例执行前执行一次"
pass
def tearDown(self):
"在每次测试用例执行完成后执行一次"
pass
@classmethod
def setUpClass(cls):
"在每次测试用例类执行前执行一次"
@classmethod
def tearDownClass(cls):
"在每次测试用例类执行后执行一次"
...........复制
方法详解
方法 | 描述 | 示例 |
setUp(self) | 在每次测试用例执行前执行一次 | |
tearDown(self) | 在每次测试用例执行完成后执行一次 | |
setUpClass(cls) | 在每次测试用例类执行前执行一次 | 需要使用@classmethod装饰 |
tearDownClass(cls) | 在每次测试用例类执行后执行一次 |
示例1:
重写TestCase中的setUp tearDown
import unittest
class MyTest(unittest.TestCase):
def setUp(self):
print("每条测试用例开始执行前执行")
def test_case(self):
print("我是测试用例1")
def tearDown(self):
print("每条测试用例执行结束后开始执行")复制
可以看到setUp方法是在每条用例开始前执行一次、tearDown方法是在每条测试用例执行后都会执行一次
示例2:
重写TestCase中的setUpClass tearDownClass
在使用setUpClass与tearDownClass二个方法前需要进行使用@classmethod装饰才生效
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
import unittest
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("整个测试类中之在开始执行测试方法前执行一次")
def test_case(self):
print("我是测试用例1")
def test_case1(self):
print("我是测试用例2")
@classmethod
def tearDownClass(cls):
print("在整个测试类中所有测试用例执行完后才执行")
if __name__ == '__main__':
unittest.main()复制