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

在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

 

 

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值