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)
View Code

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
View Code

这个方法内部实例化runner.TextTestRunner, 然后将createTests嗅探到的测试套self.test传递给runner.

runner的构造方法接收运行参数, __call__方法接收self.test并开始驱动测试套运行.

 

 

  

posted @ 2021-02-09 16:41  O万山  阅读(175)  评论(0编辑  收藏  举报