单元测试中如何断言父装饰器的包装函数在条件为真时被执行?
单元测试中如何断言父装饰器的包装函数在条件为真时被执行?
我明白你的问题所在——你的测试里mockParent.call_count总是返回1,根本原因是装饰器的初始化时机:当你用@conditionalDecorator(...)装饰函数的时候,childDecorator内部已经直接调用了parentDecorator(func),这一步是在装饰阶段完成的,和后续调用函数时的条件判断完全无关。所以不管条件是True还是False,parentDecorator都会被调用一次,导致你的断言永远是1。
下面给你两种解决方案,你可以根据实际情况选择:
方案一:修改装饰器逻辑,延迟父装饰器的初始化
如果可以调整装饰器的代码,我们可以把父装饰器的初始化逻辑移到条件判断内部,只有当条件为真且第一次调用函数时,才去初始化父装饰器。这样就能保证只有条件满足时,parentDecorator才会被调用:
def conditionalDecorator(condition): def childDecorator(func): # 缓存父装饰器包装后的函数,避免重复初始化 wrapped_parent_func = None def wrapped(*args,**kwargs): nonlocal wrapped_parent_func if condition(): print(f"{condition()}") if not wrapped_parent_func: # 只有条件为真且第一次调用时,才初始化父装饰器 wrapped_parent_func = parentDecorator(func) return wrapped_parent_func(*args,**kwargs) else: print(f"{condition()}") return func(*args,**kwargs) return wrapped return childDecorator
修改后,你的原测试代码就能正常工作了:当条件为True时,mockParent.call_count会是1;条件为False时,mockParent.call_count会是0,完全符合你的断言预期。
方案二:调整测试逻辑,断言父装饰器返回的包装函数是否被执行
如果不能修改装饰器的代码,那我们可以换个测试思路——不去断言parentDecorator的调用次数(因为它在装饰阶段一定会被调用),而是断言它返回的包装函数是否被实际执行了。
具体做法是给mockParent返回一个带调用记录的Mock函数,然后通过这个Mock函数的调用情况来判断:
import unittest from unittest import mock import util import decoratorFactory class TestMyDecorator(unittest.TestCase): @mock.patch("decoratorFactory.parentDecorator") def testConditionalDecorator_modifiesOutput(self, mockParent): # 创建一个Mock的包装函数,用来记录它的调用情况 mock_wrapped_func = mock.Mock(return_value="Hello") mockParent.return_value = mock_wrapped_func # 测试条件为True的情况 with mock.patch("util.get_my_condition", return_value=True): @decoratorFactory.conditionalDecorator(condition=util.get_my_condition) def testInnerFunction(): return "Hello" result = testInnerFunction() self.assertEqual(result, "Hello") # 父装饰器在装饰阶段会被调用一次 self.assertEqual(mockParent.call_count, 1) # 关键断言:父装饰器返回的包装函数被实际执行了 mock_wrapped_func.assert_called_once() # 测试条件为False的情况 with mock.patch("util.get_my_condition", return_value=False): @decoratorFactory.conditionalDecorator(condition=util.get_my_condition) def testInnerFunction2(): return "Hello" result = testInnerFunction2() self.assertEqual(result, "Hello") # 父装饰器还是会被调用一次(装饰阶段) self.assertEqual(mockParent.call_count, 2) # 断言:包装函数没有被执行(调用次数还是1,和之前的True情况一致) mock_wrapped_func.assert_called_once() if __name__=='__main__': unittest.main()
这样就能清晰区分两种情况:当条件为True时,父装饰器的包装函数被实际调用;当条件为False时,虽然父装饰器被初始化了,但包装函数根本没被执行。
内容来源于stack exchange
相关产品推荐
相关产品推荐

