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
很明显, 这完全就是个执行驱动.所有的方法都是针对测试执行过程的.
* 模块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
* 主逻辑:
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方法. 执行用例级别用例函数.
原文:https://www.cnblogs.com/woruo/p/14393782.html