- 什么是单元测试?
单元测试是对最小的软件设计单元(模块、类)进行验证,它使用开发文档中对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误。
- 单元测试框架可以解决说明问题?
1、提供用例组织与执行
2、提供丰富的断言方法
3、提供丰富的日志
- 重要概念
1.Test Case 测试用例
继承unittest.TestCase的子类里的每个名为test*的函数都被视为单个测试用例,前后执行setUp()和tearDown()
2.Test Suite 测试用例集合——把多个测试用例集合在一起执行,可以通过addTest加载TestCase到TestSuite中。
-单元测试的加载方式有2种:一种是通过unittest.main()来启动单元测试的测试模块(文件);1.被测试类继承了unittest.TestCase;2.被测试的函数名为test*
-一种是添加到testsuite集合中再加载所有的被测试对象,而testsuit里存放的就是单元测试的用例。
(另一种TestLoader类的dicover方法直接加载文件中所有被测试对象,其实也应用了testsuite原理)

1 # -*-coding:utf-8-*- 2 3 import unittest 4 5 def setUpModule(): # 模块的前后执行 6 print("测试模块开始-------") 7 def tearDownModule(): 8 print("测试模块结束=======") 9 10 class Test(unittest.TestCase): 11 12 @classmethod 13 def setUpClass(cls): # 类的前后执行 14 print("测试类开始------") 15 @classmethod 16 def tearDownClass(cls): 17 print("测试类结束=======") 18 19 def setUp(self): # 函数的前后执行 20 print("测试用例开始------") 21 def tearDown(self): 22 print("测试用例结束======") 23 def test_case(self): 24 print("我是用例") 25 26 unittest.main()
3.Test Runner 执行
通过TextTestRunner类提供的run()方法来执行test suite/test case,test runner可以使用图形界面、文本界面,或返回一个特殊的值方法来表示测试执行的结果。
runner = unittest.TextTestRunner()
runner.run(xxx)
4.Test Fixture 执行前后的操作setUp()和tearDown()

1 # -*-coding:utf-8-*- 2 3 import unittest 4 5 def setUpModule(): 6 print("测试模块开始-------") 7 def tearDownModule(): 8 print("测试模块结束=======") 9 10 class Test(unittest.TestCase): 11 12 @classmethod 13 def setUpClass(cls): 14 print("测试类开始------") 15 @classmethod 16 def tearDownClass(cls): 17 print("测试类结束=======") 18 19 def setUp(self): 20 print("测试用例开始------") 21 def tearDown(self): 22 print("测试用例结束======") 23 def test_case(self): 24 print("我是用例") 25 26 unittest.main()
- 断言方法
unittest的TestCase类提供下面方法用于测试结果的判断:
断言语法 | 解释 |
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) 断言a是b的一个实例 |
assertNotIsInstance(a, b) | not isinstance(a, b) 断言a不是b的实例 |
- assertEqual(a , b , msg=None)
断言第一个参数a和第二个参数b是否相等,如果不等则测试失败Fail。msg为可选参数,用于定义测试失败时打印的信息。
- TestLoader类
discover以py文件(模块)化方式一次执行模块里所有名为test*的函数。替代了suite.addTest()。
正常情况下,不需要创建这个类的实例。unittest提供了可以共享的defaultTestLoader类,可以直接使用。
- unittest.defaultTestLoad.discover(star_dir, pattern='test*.py', top_level_dir=None)
- 注意:每个文件夹中必须创建__init__.py文件,discover才能识别文件夹里名为test*.py的文件。

1 import unittest 2 3 # discover()方法会匹配指定当前目录下./的所有test*.py的用例文件,并将查找到的测试用例组装到测试套件赋在discover中 4 5 test_dir = './' 6 discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py') 7 8 # 直接通过run()方法执行 9 10 if __name__ == '__main__': 11 runner = unittest.TextTestRunner() 12 runner.run(discover)
- 用例执行顺序
unnitest框架默认根据同级目录下模块或文件名的ASCII码的顺序来加载测试用例,所以只能通过命名来改变执行顺序。除非用TestSuite类的addTest()方法按照别的顺序加载。
文件 > 类 >函数
A~Z,a~z,0~9,
例:(当用discover时)
aaa文件夹-testsub.py
testadd.py
-aaa文件与testadd.py同级,但aaa的ASCII码<testadd的ASCII码,所以aaa文件夹-testsub.py要优先于testadd.py!
- unittest如何识别多层目录?
在每个目录加上__init__.py文件,就可以识别当前目录以下的所有文件——一个包是一个带有特殊文件 __init__.py 的目录。__init__.py 文件定义了包的属性和方法。其实它可以什么也不定义;可以只是一个空文件,但是必须存在。如果 __init__.py 不存在,这个目录就仅仅是一个目录,而不是一个包,它就不能被导入或者包含其它的模块和嵌套包。
Python中__init__.py文件的作用详解 http://www.jb51.net/article/92863.htm
- 跳过测试和预期失败

1 # -*-coding:utf-8-*- 2 import unittest 3 4 class MyTest(unittest.TestCase): 5 @unittest.skip("必输入reason") # 无条件跳过测试,说明原因 6 def test_skip1(self): 7 print('直接跳过测试~') 8 @unittest.skipIf(3>2,"必输入reason") # 如果条件为真,跳过测试 9 def test_skip2_if(self): 10 print('条件为真,则跳过测试~') 11 @unittest.skipUnless(3>2,"必输入reason") # 如果条件为假,跳过测试 12 def test_skip3_unless(self): 13 print('条件为假,则跳过测试') 14 @unittest.expectedFailure # 不管结果如何,不抛错 15 def test_excepted_failure(self): 16 self.assertEqual(2, 2) 17 18 19 unittest.main()
- 保存测试结果报告
打开cmd,到runtest.py模块的当前目录,新建文件夹report,输入:
> python runtest.py >> report/log.txt 2>&1
发现生成了一个report文件夹里的log.txt为结果报告。