想要做 OP 正在做的事情的另一个原因是创建一个高度参数化的基类,它实现了一组需要在多个环境/场景中重现的核心测试。我所描述的本质上是使用 unittest 创建一个参数化的fixture,一个 la pytest。
假设您(像我一样)决定尽可能快地逃离任何基于多重继承的解决方案,使用 load_tests() 从加载的套件中过滤掉您的基类时可能会遇到以下问题:
在标准的TestLoader 中,load_tests 被调用在自动加载类完成之后。因为:
* 此自动加载类将尝试使用标准签名 init(self, name) 从您的基类构造实例,并且
* 你可能希望这个基类有一个非常不同的 ctor 签名,或者
* 您可能出于其他原因希望跳过构建然后删除基类实例
.. 您可能希望完全阻止从基类自动加载测试实例。
编辑:Vadim's solution in this other thread 是一种更优雅、简洁和独立的方式来执行此操作。我已经实现了“嵌套类技巧”,并确认它可以很好地防止 TestLoader “找到”您的 TestCase 基础。
我最初是通过修改 TestLoader.loadTestsFromModule 来简单地跳过作为模块中任何其他 TestCase 类的基类的任何 TestCase 类来做到这一点的:
for name in dir(module):
obj = getattr(module, name)
# skip TestCase classes:
# 1. without any test methods defined
# 2. that are base classes
# (we don't allow instantiating TestCase base classes, which allows test designers
# to implement actual test methods in highly-parametrized base classes.)
if isinstance(obj, type) and issubclass(obj, unittest.TestCase) and \
self.getTestCaseNames(obj) and not isbase(obj, module):
loaded_suite = self.loadTestsFromTestCase(obj)
# ignore empty suites
if loaded_suite.countTestCases():
tests.append(loaded_suite)
地点:
def isbase(cls, module):
'''Returns True if cls is base class to any classes in module, else False.'''
for name in dir(module):
obj = getattr(module, name)
if obj is not cls and isinstance(obj, type) and issubclass(obj, cls):
return True
return False
我上面提到的参数化是通过让每个子类定义它的夹具细节(参数)并将它们传递给基类 TestCase ctor 来实现的,这样它的所有公共 impl 方法(“fixturey”那些 setUp*/ tearDown*/cleanup* 和测试方法本身)具有定义该子 TestCase 类要操作的现在非常具体的夹具的所有信息。
对我来说,这是在 unittest 中快速实现一些参数化固定装置的临时解决方案,因为我计划尽快将团队的测试转移到 pytest。