unittest测试框架学习和源码走读(1)
I.unitest源码
unittest------.. | __init__.py | __main__.py | case.py # 用例组织模块 | loader.py # 测试用例嗅探模块 | main.py # 脚本主入口 | result.py # 测试结果维护 | runner.py # 测试运行 | signal.py # 信号量处理 | suite.py # 测试套 | uti.py # 公用工具
unitest包按照抽象模型组织为以上结构, 接下来将对主要抽象对象进行解析.
说明: 为便于穿层分析, 以最简单的测试用例代码为例:
demo.py
import unittest class TestStringMethods(unittest.TestCase): def test_upper(self): u"validate if fool upper is FOOL" self.assertEqual('fool'.upper(), "FOOL") print("greeting") def test_isUpper(self): u"validate if fool isupper" self.assertTrue("FOOL".isupper()) self.assertFalse("Fool".isupper()) print("say hi") def test_split(self): u"validate if s.split get integer" s = "hello world" self.assertEqual(s.split(), ["hello", "world"]) with self.assertRaises(TypeError): s.split(2) print("called") if __name__ == "__main__": unittest.main(verbosity=4)
II.测试脚本模块main.py
作为命令行工具, 该脚本主要代码都在处理函数式调用和命令行参数, 将参数解析为模块的运行参数.命名也很直白.
按照demo.py的运行方式, unittest.main()直接调用TestProgram的构造函数.
1.构造函数内部, 调用parseArgs解析命令行参数, parseArgs调用createTests函数.
2.createTests通过loader解析模块, 获取到TestSuite实例.
3.构造函数调用runTests方法, 执行测试套.
class TestProgram(object): USAGE = USAGE_FROM_MODULE failfast = catchbreak = buffer = progName = None def __init__(self, module="__main__", defaultTest=None, argv=None, testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None): pass def usageExit(self, msg=None): pass def parseArgs(self, argv): pass def createTests(self): pass def _do_discovery(self, argv, loader=None): pass def runTests(self): pass
4.runTests方法
def runTests(self): if self.catchbreak: installHandler() if self.testRunner is None: self.testRunner = runner.TextTestRunner if isinstance(self.testRunner, (type, types.ClassType)): try: testRunner = self.testRunner(verbosity=self.verbosity, failfast=self.failfast, buffer=self.buffer) except TypeError: # didn't accept the verbosity, buffer or failfast arguments testRunner = self.testRunner() else: # it is assumed to be a TestRunner instance testRunner = self.testRunner self.result = testRunner.run(self.test) if self.exit: sys.exit(not self.result.wasSuccessful()) main = TestProgram
这个方法内部实例化runner.TextTestRunner, 然后将createTests嗅探到的测试套self.test传递给runner.
runner的构造方法接收运行参数, __call__方法接收self.test并开始驱动测试套运行.