unittest是Python内置的单元测试框架,不仅可以完成单元测试,也适用于自动化测试中,其提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果报告。
unittest重点
学习重点在于掌握unittest中的几个关键的类:
- unittest.TestCase : 测试用例的基类,指定测试用例方法名字,返回测试用例实例
- unittest.TestSuite : 测试用例的容器(套件),可理解为测试集合,支持测试的用例添加和删除
- unittest.TextTestRunner : 测试用例的执行器,以文本形式将保存到TextTestResult中
- unittest.TestLoader : 测试用例的装载器,将测试用例(TestCase)装载到测试容器(TestSuite)中。
- unittest.defaultTestLoader :默认装载器,本质是TestLoader装载器的实例化,与TestLoader 等同
- unittest.TestProgram:TestProgram类名被赋值给了main变量,然后通过unittest.main()的形式调用
unittest.TestCase
1 | import unittest # 导入unittest框架 |
从上述代码分析执行流程,首先执行用例前会使用setUp进行初始化操作,然后执行测试用例,最终使用tearDown进行收尾工作
注意:
- myUnitTest类名可以自定义,但是必须继承
unittest.TestCase
。 - setUp和tearDown方法名是固定的,测试用例时,如果没有初始化和收尾的工作,setUp和tearDown方法可以省略不写。
- 在每个用例执行时,setUp和tearDown都会执行
为什么实例化了myUnitTest调用run方法就执行了,原因当然要从其继承的父类里找问题,可以看到
1 | class TestCase(object): |
在TestCase实例化的时候,有个methodName
默认参数,默认为runTest。而在实例化后,实例化对象调用run方法的时候,反射了那个methodName
值,所以用例执行了
因此引出我们可以通过传参methodName来自定义我们用例的名称
1 | mytest = myUnitTest(methodName='new_test') |
但是这种原始的写法也太low了,一般是不会这样写的。
unittest断言
使用框架测试,其最重要的就是使用断言来判断用例是否预期与实际匹配
unittet.TestCase
提供了一些断言方法用来检查并报告故障。
方法 | 类比 | 描述 | 版本 |
---|---|---|---|
assertEqual(a, b, msg) | a == b | 如果a不等于b,断言失败 | |
assertNotEqual(a, b, msg) | a != b | 如果a等于b,断言失败 | |
assertTrue(x, msg) | bool(x) is True | 如果表达式x不为True,断言失败 | |
assertFalse(x, msg) | bool(x) is False | 如果表达式x不为False,断言失败 | |
assertIs(a, b, msg) | a is b | 如果a is not 2,断言失败 | 3.1 |
assertIsNot(a, b, msg) | a is not b | 如果a is b,断言失败 | 3.1 |
assertIsNone(x, msg) | x is not None | 如果x不是None,断言失败 | 3.1 |
assertIn(a, b, msg) | a in b | 如果a not in b,断言失败 | 3.1 |
assertNotIn(a, b, msg) | a not in b | 如果a in b,断言失败 | 3.1 |
assertIsInstance(a, b, msg) | isinstance(a, b) | 如果a不是b类型,断言失败 | 3.2 |
assertNotIsInstance(a, b, msg) | not isinstance(a, b) | 如果a是b类型,断言失败 | 3.2 |
注:assert方法都接收一个msg参数,如果指定,该参数将用作失败时的错误提示
unittest.TestSuite
测试套件(test suite)是由许多测试用例组成的复合测试,也可以理解为承载多个用例集合的容器。
使用时需要创建一个TestSuite实例对象,然后使用该对象添加用例:
- suite_obj.addTest(self, test),添加一个测试用例。
- suite_obj.addTests(self, tests),添加多个测试用例。
- 在实例化方法中添加测试用例。
其有效地将多个用例组织到一起进行集中测试,解决了单个用例测试的问题。
使用方式:
1 | # 实例化suite对象 |
↓
一个一个添加用例未免太傻了,使用python高阶函数
1 | map_obj = map(myUnitTest, ['case1', 'case2']) |
↓
实例化时候添加用例
1 | map_obj = map(myUnitTest, ['case1', 'case2']) |
以上都需要手动的将用例添加到suite的中,太累了,自动添加
↓
unittest.MakeSuite
想要自动添加,需要使用unittest.makeSuite
类来完成,在实例化unittest.makeSuite(testCaseClass, prefix='test')
时,需要告诉makeSuite添加用例的类名,然后makeSuite将testCaseClass类中所有以prefix参数指定开头的用例(默认读取以test开头),自动添加到suite中。
1 | suite_obj = unittest.makeSuite(myUnitTest, prefix='xxx') |
当然也可以同时手动补充用例
1 | suite_obj = unittest.makeSuite(myUnitTest, prefix='xxx') |
unittest.TestLoader
以上其所有的用例方法都封装在一个用例类中,但实际中我们会根据不同的功能编写不同的测试用例文件,存放在不同的目录内
这个时用addTest添加就非常的麻烦了,unittest提供了TestLoader类来解决这个问题。
TestLoader提供的方法:
- TestLoader.loadTestsFromTestCase,返回testCaseClass中包含的所有测试用例的suite。
- TestLoader.loadTestsFromModule,返回包含在给定模块中的所有测试用例的suite。
- TestLoader.loadTestsFromName,返回指定字符串的所有测试用例的suite。
- TestLoader.loadTestsFromNames,返回指定序列中的所有测试用例suite。
- TestLoader.discover,从指定的目录开始递归查找所有测试模块。
拿工作中常用的来写,TestLoader.discover
discover的语法:
1 | discover = unittest.TestLoader().discover( |
通过TestLoader()
实例化对象,然后通过实例化对象调用discover方法,discover根据给定目录,递归找到子目录下的所有符合规则的测试模块,然后交给TestSuit生成用例集suite,最后交给TextTestRunner执行用例。
该discover方法接收的三个参数:
- start_dir:要测试的模块名或者测试用例的目录
- pattern=”test*.py”:表示用例文件名的匹配原则,默认匹配以
test
开头的文件名,星号表示后续的多个字符 - top_level_dir=None:测试模块的顶层目录,如果没有顶层目录,默认为None
注:discover对给定的目录是有要求的,它只识别Python的包,也就是目录内有__init__.py
文件的才算是Python的包,只要是要读取的目录,都必须是包。
关于start_dir和top_level_dir的几种情况:
- start_dir目录可以单独指定,这个时候,让top_level_dir保持默认(None)即可。
- start_dir == top_level_dir, start_dir目录与top_level_dir目录一致,discover寻找start_dir指定目录内的符合规则的模块。
- start_dir < top_level_dir,start_dir目录是top_level_dir目录的子目录。discover寻找start_dir指定目录内的符合规则的模块。
- start_dir > top_level_dir,start_dir目录如果大于top_level_dir目录,等待你的是报错
AssertionError: Path must be within the project
。说你指定的路径(start_dir)必须位于项目内(top_level_dir)。
另外unittest已经帮我们实例化好了TestLoader对象—>defaultTestLoader,我们可以直接使用defaultTestLoader.discover
1 | discover = unittest.defaultTestLoader.discover( |