You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试中如何断言父装饰器的包装函数在条件为真时被执行?

单元测试中如何断言父装饰器的包装函数在条件为真时被执行?

我明白你的问题所在——你的测试里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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:23:06