测试含threading.Timer的函数时mock在定时器线程失效如何解决
问题解决方法
问题根因
- 你当前的测试没有等待
Timer启动的异步任务执行完成,主线程跑完open方法就直接执行断言,此时_check_order_status逻辑根本还没触发,哪怕mock配置是对的断言也会失败 - 直接patch类方法的写法本身是线程安全的,只要patch未失效,子线程也能读取到mock后的方法,问题本质是测试执行时序不对,加上异步等待增加了不稳定因素
注意:你原代码open方法的参数顺序是(volume: float, type: str),你调用时传的order.open('BUY', 123)参数顺序颠倒,需要先修正这个问题
推荐解决方案
方案1:Mock Timer类,同步执行异步逻辑(最推荐,单元测试避免真实等待)
单元测试不需要真实执行定时器等待,直接mock掉Timer类,让它的start方法立刻执行传入的目标函数,既不会有线程上下文问题,也能省掉等待时间,测试更稳定。
示例测试代码:
def test_open(mocker: MockFixture, new_open: Dict, get_open_order: Dict): # Arange # 1. mock Timer,直接同步执行目标函数,跳过延迟和线程创建 def mock_timer(interval, target): class MockTimer: def start(self): target() return MockTimer() mocker.patch("module.Timer", mock_timer) # 2. mock client方法 def mock_create_order(self, type, quantity): return new_open mocker.patch('module.Client.create_order', mock_create_order) def mock_get_order(self, orderId): return get_open_order mocker.patch('module.Client.get_order', mock_get_order) # Act order = Order() # 修正参数顺序:volume在前,type在后 order.open(123, 'BUY') # Assert assert len(mock_state.open) == 1
方案2:替换实例的_client属性为Mock对象
如果不想修改Timer的行为,直接给Order实例的_client属性赋值为你提前构造好的mock对象,不管哪个线程访问这个实例的_client都会用mock后的方法,不受patch作用域影响:
def test_open(mocker: MockFixture, new_open: Dict, get_open_order: Dict): # Arange # 构造mock client对象 mock_client = Mock() mock_client.create_order.return_value = new_open mock_client.get_order.return_value = get_open_order # Act order = Order() # 直接替换实例的client属性,优先级高于类级别的patch order._client = mock_client order.open(123, 'BUY') # 等待Timer执行完成,至少等够1秒+逻辑执行时间 import time time.sleep(1.5) # Assert assert len(mock_state.open) == 1
方案3:保持真实Timer,同步等待执行完成
你也可以在测试中加同步逻辑,等待异步任务执行完成后再断言,需要注意patch的作用域要覆盖到任务执行完成的时间,但是这种方案测试执行慢,容易因为环境波动失败,不推荐。
内容的提问来源于stack exchange,提问作者exilonX
相关产品推荐
相关产品推荐

