如何使用pytest patch装饰器测试Ray Actor的远程函数
Ray 非本地模式下远程Actor方法打补丁失败的解决方案
问题原因
Ray 非本地模式运行时,所有 Actor 都会被调度到独立的 Worker 进程中执行,测试主进程通过
@patch打的补丁仅作用于主进程内存空间,不会同步到远程 Worker 进程,因此远程 Actor 调用的还是原始方法,导致测试失败。本地模式下所有逻辑都在同一进程运行,所以补丁可以正常生效。
解决方案
以下两种方案都不需要开启local_mode即可生效:
方案1:Actor初始化时主动打补丁
在远程Actor实例化阶段,先在当前Worker进程执行补丁逻辑,适配单个测试用例的补丁需求:
# 测试文件新增:Worker进程执行的补丁逻辑 def patch_foo_bar(): from unittest.mock import patch # 注意导入必须放在函数内部,保证Worker进程可以加载到对应模块 from foo import Foo patcher = patch("foo.Foo.bar", new=fake_bar) patcher.start() # 新增带补丁的Actor包装类 @ray.remote class PatchedFoo(Foo): def __init__(self): # 实例化时先在当前Worker打补丁 patch_foo_bar() super().__init__() # 修改后的测试用例 def test_bar_remote(self): foo_actor = PatchedFoo.remote() obj_ref = foo_actor.bar.remote() ray.get(obj_ref)
方案2:全局Worker启动钩子批量打补丁
如果需要给所有测试用例的Worker都打上相同补丁,可以在Ray初始化时配置启动钩子,所有Worker进程启动后会自动执行补丁逻辑:
# 复用上面定义的patch_foo_bar函数 @pytest.fixture def ray_fixture(): print("Initializing ray") if not ray.is_initialized(): ray.init( runtime_env={ # 配置Worker进程启动钩子 "worker_process_setup_hook": patch_foo_bar } ) yield None print("Terminating ray") ray.shutdown()
配置后原有的test_bar_remote代码不需要任何修改即可正常运行。
注意事项
- 所有需要在Worker进程执行的函数(补丁函数、fake方法等)都必须定义在模块顶层,保证Ray可以正常序列化和在Worker端导入
- 补丁逻辑内的模块导入必须放在函数内部,避免主进程和Worker进程的导入路径不一致导致的报错
内容的提问来源于stack exchange,提问作者user1264641
相关产品推荐
相关产品推荐

