unittest测试框架学习和源码走读(3)

V.测试套模块suite.py

    测试套模设计的比较尴尬, 按照名称应该是测试套件的组织,应该只有一个协议或者范式声明. 然鹅, 他和

  用例模块case.py也身兼用例组织和用例驱动执行的功能. 甚至, suite范式声明的码量很少. 大部分工作是测试

  执行.  

    基类BaseTestSuite功能很单一, 构造测试套& 增加测试用例. 给子类预留了可执行hook BaseTestSuit.run.

  TestSuite纯粹就是个执行驱动器, 需要处理测试 模块&类&函数的lifecycle.

class TestSuite(BaseTestSuite):
    def run(self, result, debug=False):
         pass

    def debug(self):
        pass

   def _handleClassSetUp(self, test, result):
        pass

    def _get_previous_module(self, result):
        pass

    def _handleModuleFixture(self, test, result):
        pass

   def _addClassOrModuleLevelExecption(self, test, result):
        pass

   def _handleModuleTearDown(self, test, result):
        pass

   def _tearDownPreviousClass(self, test, result):
        pass
View Code

  很明显, 这完全就是个执行驱动.所有的方法都是针对测试执行过程的.

* 模块fixture

    模块即python包, testsuite隐式的定义一对模块fixture(setUpModule, tearDownModule). 很少被用到, 甚至官

  方文档也介绍甚少. 但是, 在用例组织的更高层级大有可为. 如: 测试包的公共前置动作, 注册测试帐号&创建单例

  共享资源. 他们隐含在函数:

  -handleModuleFixture(self, test, result):  

  -handleModleTearDown(self, result): 

  这对命名不对称, 万马奔腾.

* 类fixture

          类case.TestCase派生类,显式的定义了一对类fixture(setUpClass, tearDownClass). 用的比较多的类级共享资

  源.如: 测试云主机, setUpClass创建云主机绑定公网ip并建立ssh连接. 用例对云主机做变更测试, tearDownClass

  做云主机和相关资源的清理动作. 这样做是符合测试设计等价类预期的: i).复用资源, 用少量的资源和时间覆盖最多

  的测试场景;ii).叠加场景, 同一资源在状态切换的前后动作反复折腾, 可以覆盖单一测试用例cover不住的测试情景.

  iii).如果再打乱用例, 从测试覆盖来说可以弥补手工测试扁平&有限用例设计的天然缺陷.

* 函数run

    run函数才是真正驱动测试类和测试模块运行的机器, 实现非常的优雅. 仅引入一个外部寄存布尔值和一个局部

  变量便实现了执行驱动.

def run(self, result, debug=False):
    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    
View Code

  * 主逻辑: 

  1.进入函数, result._testRunEntered=False(result.TestResult默认值), 通过result记录本测试套本层已经开始执行.退出

函数前, 通过topLevel判断是否需要清理类fixture和模块fixture.

  2.主循环, 父类实现了__iter__函数, 将TestSuite的迭代代理给列表self._tests, 代码更加简洁优雅但是并不直观. 对于循

环体test, 由于TestSuite的递归特性, 可能是TestSuite类也可能是TestCase类. 

  * test是测试套, 则直接调用test(result)函数. test(result)是谁呢? 回顾前面load.py模块 , 最后的结论是实例化之后的case.

TestCase或者suite.TestSuite本尊. 调用TestSuite实例的__call__方法不就是调用suite.TestSuite.run(self, result, debug=Fals

e)自己! 这个递归操作太骚了...

  * test是类, 则进入到if _isnotsuite(test):分支. 模块开始前, 要把前一个模块的最后一个类fixture清理掉,把前一个模块的fix

ture清理掉, 再构建自己家的fixture. 完事之后告诉result, 以后有人问你前面那货是谁就说是劳资.这个时候调用的test(result)

又是谁呢?此时的test是case.TestCase实例, 调用的是case.TestCase.__call__. 很遗憾, 又代理给了run方法.不过这次的run是

TestCase的run, 真是是执行用例了.

  至次, 模块和类级别的fixture和执行逻辑完毕. 执行权限移交给case.TestCase.run方法. 执行用例级别用例函数.

 

posted @ 2021-02-10 10:51  O万山  阅读(119)  评论(0编辑  收藏  举报