python unittest源码解析一----测试用例是如何被执行的
2015-09-01 19:22
756 查看
在Python的单元测试中,有各种不同方式来执行用户的测试用例,在接下来的篇幅中,我们会详细叙述每种方式的具体执行流程。
先来看下unittest中的__init__.py中提供的一个测试用例案例:
可以把上面的用例放到一个arithmetic.py中,然后执行命令python arithmetic.py,测试用例会被依次运行。
那么,unittest是怎么提取相应的测试用例进行的运行的呢?
先来看一下unittest下面有哪些文件,分别是:__init__.py,__main__.py,case.py,loader.py,main.py,result.py,runner.py,signals.py,suite.py,utils.py。
这里先不描述这些文件都有什么用,因为在执行流程的叙述中自然会说明白它们的作用。
1.
unittest.main()开始了整个单元测试,这里实际上是调用了main.py中的TestProgram类的构造函数,因为在该文件中有这样一条语句:main = TestProgram。
这里是为了执行测试用例所在的module。再来看下另外个参数testLoader=loader.defaultTestLoader,在loader.py中设置了defaultTestLoader = TestLoader()。
构造函数中进行了一系列的初始化操作,最后分别执行self.parseArgs(argv)和runTests()去提取和执行测试用例。
self.parseArgs(argv)会执行self.createTests(),如下
这里testNames是None,所以会执行:
进入loader.py的loadTestsFromModule:
这里是把self.loadTestsFromTestCase(obj)得到suite追加到tests列表中,然后把tests也转化为suite,并且返回。(这里面包含了所有的TestCase)
2.
下面来看看self.runTests()怎么执行
在runTests()中会执行testRunner.run(self.test),也就是说会去执行TextTestRunner中的run方法,并把原先获取的TestSuite对想传入。
runner.py
在run中,通过一个for循环依次调用TestCase的run方法去执行具体的测试用例(这里要注意的是,如果test还是TestSuite类型,会继续递归调用)。
case.py
进入TestCase的run方法:
获取测试方法
执行获取到的测试方法
先来看下unittest中的__init__.py中提供的一个测试用例案例:
import unittest class IntegerArithmeticTestCase(unittest.TestCase): def testAdd(self): ## test method names begin 'test*' self.assertEqual((1 + 2), 3) self.assertEqual(0 + 1, 1) def testMultiply(self): self.assertEqual((0 * 10), 0) self.assertEqual((5 * 8), 40) if __name__ == '__main__': unittest.main()
可以把上面的用例放到一个arithmetic.py中,然后执行命令python arithmetic.py,测试用例会被依次运行。
那么,unittest是怎么提取相应的测试用例进行的运行的呢?
先来看一下unittest下面有哪些文件,分别是:__init__.py,__main__.py,case.py,loader.py,main.py,result.py,runner.py,signals.py,suite.py,utils.py。
这里先不描述这些文件都有什么用,因为在执行流程的叙述中自然会说明白它们的作用。
1.
unittest.main()开始了整个单元测试,这里实际上是调用了main.py中的TestProgram类的构造函数,因为在该文件中有这样一条语句:main = TestProgram。
def __init__(self, module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None): print str(self.__class__) + " " + str(sys._getframe().f_lineno) if isinstance(module, basestring): print str(self.__class__) + " " + str(sys._getframe().f_lineno) self.module = __import__(module) for part in module.split('.')[1:]: self.module = getattr(self.module, part) else: self.module = module if argv is None: argv = sys.argv self.exit = exit self.failfast = failfast self.catchbreak = catchbreak self.verbosity = verbosity self.buffer = buffer self.defaultTest = defaultTest self.testRunner = testRunner self.testLoader = testLoader self.progName = os.path.basename(argv[0]) self.parseArgs(argv) self.runTests()上面是TestProgram的构造函数,这里的module默认是__main__,然后会去动态加载当前的module,即self.module = __import__(module)。
这里是为了执行测试用例所在的module。再来看下另外个参数testLoader=loader.defaultTestLoader,在loader.py中设置了defaultTestLoader = TestLoader()。
构造函数中进行了一系列的初始化操作,最后分别执行self.parseArgs(argv)和runTests()去提取和执行测试用例。
self.parseArgs(argv)会执行self.createTests(),如下
def createTests(self): if self.testNames is None: self.test = self.testLoader.loadTestsFromModule(self.module) else: self.test = self.testLoader.loadTestsFromNames(self.testNames, self.module)
这里testNames是None,所以会执行:
self.testLoader.loadTestsFromModule(self.module)
进入loader.py的loadTestsFromModule:
def loadTestsFromModule(self, module, use_load_tests=True): """Return a suite of all tests cases contained in the given module""" tests = [] print module for name in dir(module): obj = getattr(module, name) if isinstance(obj, type) and issubclass(obj, case.TestCase): tests.append(self.loadTestsFromTestCase(obj)) load_tests = getattr(module, 'load_tests', None) tests = self.suiteClass(tests) if use_load_tests and load_tests is not None: try: return load_tests(self, tests, None) except Exception, e: return _make_failed_load_tests(module.__name__, e, self.suiteClass) return tests
这里是把self.loadTestsFromTestCase(obj)得到suite追加到tests列表中,然后把tests也转化为suite,并且返回。(这里面包含了所有的TestCase)
2.
下面来看看self.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())
在runTests()中会执行testRunner.run(self.test),也就是说会去执行TextTestRunner中的run方法,并把原先获取的TestSuite对想传入。
runner.py
try: test(result) finally: stopTestRun = getattr(result, 'stopTestRun', None) if stopTestRun is not None: stopTestRun()这里会回调TestSuite中的run方法。
def __call__(self, *args, **kwds): return self.run(*args, **kwds)
def run(self, result, debug=False): print str(self.__class__) + " " + str(sys._getframe().f_lineno) + "\n" topLevel = False if getattr(result, '_testRunEntered', False) is False: result._testRunEntered = topLevel = True for test in self: if result.shouldStop: break if _isnotsuite(test): self._tearDownPreviousClass(test, result) self._handleModuleFixture(test, result) self._handleClassSetUp(test, result) result._previousTestClass = test.__class__ if (getattr(test.__class__, '_classSetupFailed', False) or getattr(result, '_moduleSetUpFailed', False)): continue if not debug: test(result) else: test.debug() if topLevel: self._tearDownPreviousClass(None, result) self._handleModuleTearDown(result) result._testRunEntered = False return result
在run中,通过一个for循环依次调用TestCase的run方法去执行具体的测试用例(这里要注意的是,如果test还是TestSuite类型,会继续递归调用)。
case.py
def __call__(self, *args, **kwds): return self.run(*args, **kwds)
进入TestCase的run方法:
获取测试方法
testMethod = getattr(self, self._testMethodName)
执行获取到的测试方法
else: try: testMethod() except KeyboardInterrupt: raise
相关文章推荐
- python实现杨辉三角(使用生成器generator)
- python+Eclipse+pydev环境搭建(转)
- Python - Headless Selenium WebDriver Tests using PyVirtualDisplay
- Python 随手记
- 基础知识(七)opencv、python、人脸框检测
- python 遇到的异常
- python 基于dns 轮询的业务检测
- 每日python(5)
- Z-Score数据标准化处理(python代码)
- python基础练习笔记
- 避免python Popen阻塞
- python 切片(slice)
- 任意格式文件转成字符串,传输之后,还原为原文件格式
- python中strip() 函数和 split() 函数的理解
- Python之os.system()找不到路径的原因
- Opencv_Python学习笔记--OpenCV中的GUI特性
- ssh批量登录并执行命令(python实现)
- python 正则表达式 学习笔记(不断补充ing)
- python的文档字符串
- python中时间格式