您的位置:首页 > 编程语言 > Python开发

python unittest源码解析一----测试用例是如何被执行的

2015-09-01 19:22 756 查看
在Python的单元测试中,有各种不同方式来执行用户的测试用例,在接下来的篇幅中,我们会详细叙述每种方式的具体执行流程。

先来看下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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: