如果您将覆盖__setattr__
正确定义为调用__setattr__
父类的,那么您可以将您的方法移植到定义其自己的自定义的类层次结构中__setattr__
:
def inject_tester_class(cls):
def __setattr__(self, name, value):
self._TesterClass__setattr_args.append((name, value))
super(intermediate, self).__setattr__(name, value)
def assertSetAttrDelegatedFor(self, name, value):
assert \
[args for args in self._TesterClass__setattr_args if args == (name, value)], \
'__setattr__(name, value) was never delegated'
body = {
'__setattr__': __setattr__,
'assertSetAttrDelegatedFor': assertSetAttrDelegatedFor,
'_TesterClass__setattr_args': []
}
intermediate = type('TesterClass', cls.__bases__, body)
testclass = type(cls.__name__, (intermediate,), vars(cls).copy())
# rebind the __class__ closure
def closure():
testclass
osa = testclass.__setattr__
new_closure = tuple(closure.__closure__[0] if n == '__class__' else c
for n, c in zip(osa.__code__.co_freevars, osa.__closure__))
testclass.__setattr__ = type(osa)(
osa.__code__, osa.__globals__, osa.__name__,
osa.__defaults__, new_closure)
return testclass
这个函数跳过了几个环节来插入一个中间类,该类将拦截任何正确委托的__setattr__
调用。即使您没有除默认值之外的任何基类,它也可以工作object
(这不会让我们替换__setattr__
为更简单的测试路径)。
它确实假设您正在使用super().__setattr__()
委托,而您在super()
没有参数的情况下使用。它还假设不涉及元类。
额外__setattr__
的以与现有 MRO 一致的方式注入;额外的中间类被注入到原始类和 MRO 的其余部分之间,并继续委托__setattr__
调用。
要在测试中使用它,您将使用上述函数生成一个新类,创建一个实例然后在该实例上设置属性:
MyTestClass = inject_tester_class(MyClass)
my_test_instance = MyTestClass()
my_test_instance.foo = 'bar'
my_test_instance.assertSetAttrDelegatedFor('foo', 'bar')
如果foo
未委托设置,AssertionError
则会引发异常,unittest
测试运行程序将其记录为测试失败。