单元测试, 即对软件设计的最小单元进行内部逻辑、语法、算法、功能等的正解性进行验证.
References:
python unittest
使用 Python Mock 类进行单元测试
测试级别:
- 代码级别
- 接口测试
- 数据结构测试
- 边界测试
- 模块功能级别
- 黑盒测试
- 其他
- 性能
- 代码规范等
测试过程:
初始用法 (单个测试用例)
# -*- coding: utf-8 -*- import unittest class Add(object): def add(self, x, y): return x + y class TestAdd(unittest.TestCase): def setUp(self): # 构造 self.obj = Add() def runTest(self): print self.obj.add(10, 20) == 30 def test_add(self): print self.obj.add(10, 20) == 30 def tearDown(self): # 析构 self.obj = None if __name__ == '__main__': # demo_add = TestAdd() # 默认执行 runTest demo_add = TestAdd('test_add') # 执行参数的方法 demo_add.run()
中级用法 (测试集)
unittest.TestSuite: 测试用例的集合, 存放测试用例的容器
添加测试集的方法:
__init__
addTest(self, test)
addTests(self, tests)
运行测试集: TextTestRunner()
简单测试集
# -*- coding: utf-8 -*- import unittest class Count(object): def add(self, x, y): return x + y def sub(self, x, y): return x - y class TestCount(unittest.TestCase): def setUp(self): # 构造 self.obj = Count() def test_add(self): print self.obj.add(10, 20) == 30 def test_sub(self): print self.obj.sub(10, 5) == 5 def tearDown(self): # 析构 self.obj = None def get_suite(): demo_count_add = TestCount('test_add') demo_count_sub = TestCount('test_sub') suite = unittest.TestSuite() suite.addTests([demo_count_add, demo_count_sub]) return suite if __name__ == '__main__': s = get_suite() print s.countTestCases() runner = unittest.TextTestRunner() runner.run(s)
结合 map
# -*- coding: utf-8 -*- import unittest class Count(object): def add(self, x, y): return x + y def sub(self, x, y): return x - y class TestCount(unittest.TestCase): def setUp(self): # 构造 self.obj = Count() def test_add(self): print self.obj.add(10, 20) == 30 def test_sub(self): print self.obj.sub(10, 5) == 10 def tearDown(self): # 析构 self.obj = None def get_suite(): case_list = ['test_add', 'test_sub'] demos = map(TestCount, case_list) suite = unittest.TestSuite() suite.addTests(demos) return suite if __name__ == '__main__': s = get_suite() print s.countTestCases() runner = unittest.TextTestRunner() runner.run(s)
使用 __init__
# -*- coding: utf-8 -*- import unittest class Count(object): def add(self, x, y): return x + y def sub(self, x, y): return x - y class TestCount(unittest.TestCase): def setUp(self): # 构造 self.obj = Count() def test_add(self): print self.obj.add(10, 20) == 30 def test_sub(self): print self.obj.sub(10, 5) == 10 def tearDown(self): # 析构 self.obj = None class CountTestSuite(unittest.TestSuite): def __init__(self): case_list = ['test_add', 'test_sub'] demos = map(TestCount, case_list) super(CountTestSuite, self).__init__(demos) if __name__ == '__main__': s = CountTestSuite() print s.countTestCases() runner = unittest.TextTestRunner() runner.run(s)
自动构建测试集
unitest.makeSuite
测试方法必须以 规定前缀 开头 (前缀可以通过 makeSuite 的 prefix 参数指定, 默认是 test)
# -*- coding: utf-8 -*- import unittest class Count(object): def add(self, x, y): return x + y def sub(self, x, y): return x - y class TestCount(unittest.TestCase): def setUp(self): # 构造 self.obj = Count() def test_add(self): print self.obj.add(10, 20) == 30 def test_sub(self): print self.obj.sub(10, 5) == 12 def tearDown(self): # 析构 self.obj = None if __name__ == '__main__': s = unittest.makeSuite(TestCount, prefix='test') print s.countTestCases() runner = unittest.TextTestRunner() runner.run(s)
高级用法
unittest.main()
自动检测测试类中所有以 test 开头的方法
使用这种方法时, 如果使用 IDLE 的话, 可能会出现下面这种错:
Traceback (most recent call last): File "C:\Users\chaoqun.zhu\Desktop\ss.py", line 32, in <module> unittest.main() File "C:\Python27\lib\unittest\main.py", line 95, in __init__ self.runTests() File "C:\Python27\lib\unittest\main.py", line 231, in runTests sys.exit(not self.result.wasSuccessful()) SystemExit: False
这是一个 IDLE 问题
因为 unittest.main()
函数会调用 sys.exit()
来结束函数进程, 类似 sys.exit(1)
表示非正常退出, sys.exist(0)
表示正常退出;
可以使用 unittest.main(exit=False)
解决.
main() 方法过程:
结果判断
继承 unittest.TestCase
就会继承下面这些方法
assert | condition |
---|---|
assertEqual(a, b) | a == b 如果 a == b 则测试通过; 如果 a != b 则发生断言; 下面的类推 |
assertNotEqual(a, b) | a != b |
assertTrue(x) | x is True |
assertFalse(x) | x is False |
assertIs(a, b) | a is b |
assertNot(a, b) | a not b |
assertIsNone(x) | x is None |
assertIsNotNone(x) | x is not none |
asertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
assertIsInstance(a, b) | instance(a, b) |
assertNoIsInstance(a, b) | not instance(a, b) |
# -*- coding: utf-8 -*- import unittest class Count(object): def add(self, x, y): return x + y def sub(self, x, y): return x - y class TestCount(unittest.TestCase): def setUp(self): # 构造 self.obj = Count() def test_add(self): self.assertEqual(self.obj.add(10, 20), 0) def test_sub(self): self.assertEqual(self.obj.sub(10, 5), 5) def tearDown(self): # 析构 self.obj = None if __name__ == '__main__': unittest.main(exit=False)
执行结果:
F. ====================================================================== FAIL: test_add (__main__.TestCount) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\chaoqun.zhu\Desktop\ss.py", line 21, in test_add self.assertEqual(self.obj.add(10, 20), 0) AssertionError: 30 != 0 ---------------------------------------------------------------------- Ran 2 tests in 0.000s FAILED (failures=1)
第一行的 F 表示有几个 FAIL, 这里只有一个.
忽略测试
@unittest.skip(reson) @unittest.skip(condition, reason) @unittest.skipUnless(condition, reason) @unittest.expectedFailure 失败不计入失败结果
@unittest.skip('Not Test') def test_sub(self): self.assertEqual(self.obj.sub(10, 5), 5) @unittest.expectedFailure def test_sub(self): self.assertEqual(self.obj.sub(10, 5), 5)
解决环境依赖 mock
向测试对象提供一套和测试资源完全相同的接口和方法, 不关心具体实现过程, 只关心具体结果.
py 3.0 + unittest.mock
py 2.7<br> pip install mock mock.Mock
关于 mock 参考: 使用 Python Mock 类进行单元测试
☠