异步/多线程代码中回调执行顺序的测试方法问询
这确实是多线程/异步测试里挺头疼的场景——没法直接在回调里插断言,还得处理跨线程的执行顺序追踪。我在实战里用过几个靠谱的方案,分享给你:
方案1:用线程/协程安全的队列记录执行轨迹
核心思路是搞一个全局的安全队列,让每个回调被触发时,把自己的唯一标识(比如函数名、自定义标签)塞进队列里。测试结束后,把队列里的记录全部取出来,和预期的顺序列表做断言就行。
这种方法的好处是完全不影响回调的业务逻辑,而且队列本身就是线程/协程安全的,能精准捕捉跨线程的执行顺序。
多线程场景示例:
import threading import pytest # 全局线程安全队列,用来存执行轨迹 execution_log = threading.Queue() def callback_a(): execution_log.put("callback_a") # 你的业务逻辑代码... def callback_b(): execution_log.put("callback_b") # 你的业务逻辑代码... def test_callback_order(): # 模拟业务逻辑的多线程调用 thread1 = threading.Thread(target=callback_a) thread2 = threading.Thread(target=callback_b) thread1.start() thread2.start() thread1.join() thread2.join() # 取出所有执行记录 actual_order = [] while not execution_log.empty(): actual_order.append(execution_log.get()) # 断言执行顺序符合预期 assert actual_order == ["callback_a", "callback_b"]
异步协程场景示例:
import asyncio import pytest execution_log = asyncio.Queue() async def callback_a(): await execution_log.put("callback_a") # 你的异步业务逻辑... async def callback_b(): await execution_log.put("callback_b") # 你的异步业务逻辑... @pytest.mark.asyncio async def test_async_callback_order(): # 启动异步任务 task1 = asyncio.create_task(callback_a()) task2 = asyncio.create_task(callback_b()) await task1 await task2 # 收集执行轨迹 actual_order = [] while not execution_log.empty(): actual_order.append(await execution_log.get()) assert actual_order == ["callback_a", "callback_b"]
方案2:用同步原语+锁保护的列表(适合严格顺序依赖场景)
如果你的业务逻辑要求某几个回调必须严格按顺序执行(比如B必须等A执行完才能跑),可以结合threading.Event/asyncio.Event和加锁的列表来同时做顺序约束和轨迹记录。
示例:
import threading import pytest # 标记A是否执行完成的事件 a_completed = threading.Event() # 加锁保护的执行顺序列表(多线程写操作必须加锁) execution_order = [] order_lock = threading.Lock() def callback_a(): with order_lock: execution_order.append("callback_a") # 你的业务逻辑... a_completed.set() # 标记A执行完成 def callback_b(): a_completed.wait() # 强制等待A完成 with order_lock: execution_order.append("callback_b") # 你的业务逻辑... def test_strict_execution_order(): # 故意先启动B,验证是否会等待A thread2 = threading.Thread(target=callback_b) thread1 = threading.Thread(target=callback_a) thread2.start() thread1.start() thread1.join() thread2.join() assert execution_order == ["callback_a", "callback_b"]
额外技巧:用装饰器包装回调(无需修改原代码)
如果回调是第三方库提供的、你没法直接修改代码,可以写一个装饰器自动给回调加上轨迹记录的逻辑:
def track_execution(log_queue): def decorator(callback): def wrapped(*args, **kwargs): # 记录回调名称 log_queue.put(callback.__name__) return callback(*args, **kwargs) return wrapped return decorator # 用装饰器包装回调,不用改原函数代码 @track_execution(execution_log) def callback_a(): # 原业务逻辑...
内容的提问来源于stack exchange,提问作者user4815162342
相关产品推荐
相关产品推荐

