Rust中如何访问被Arc包裹的mockall Mock对象以调用checkpoint方法?
解决Mockall中Arc包裹Mock后无法访问的问题
方案一:用泛型让MyStruct支持具体Mock类型
把MyStruct改成泛型结构,既能在生产环境使用实现MyTrait的具体类型,测试时也能直接持有MockMyTrait的Arc引用,方便后续操作:
#[automock] trait MyTrait { fn some_func(&self) -> i64; } // 改成泛型结构,约束T实现MyTrait struct MyStruct<T: MyTrait> { data: Arc<T> } impl<T: MyTrait> MyStruct<T> { // ... 原有代码保持不变 } #[test] fn a_test() { let mut mock = MockMyTrait::new(); mock.expect_some_func().return_const(42); // 克隆一个Arc,后续用来操作mock let mock_arc = Arc::new(mock); let testee = MyStruct { data: Arc::clone(&mock_arc) }; // 执行MyStruct的测试代码 // 通过mock_arc获取可变引用,需要用Arc::get_mut if let Some(mock_mut) = Arc::get_mut(&mut mock_arc) { mock_mut.checkpoint(); mock_mut.expect_some_func().return_const(100); } // 继续执行后续测试代码 }
这个方案类型安全,不需要额外类型转换,生产环境可以正常使用MyStruct<RealImpl>的形式。
方案二:使用Arc的downcast方法转换类型
如果不想修改MyStruct的定义,保持Arc<dyn MyTrait>的话,可以用Arc::downcast把trait对象转成具体的Mock类型:
#[automock] trait MyTrait { fn some_func(&self) -> i64; } struct MyStruct { data: Arc<dyn MyTrait> } impl MyStruct { // ... 原有代码 } #[test] fn a_test() { let mut mock = MockMyTrait::new(); mock.expect_some_func().return_const(42); let mock_arc = Arc::new(mock); let testee = MyStruct { data: Arc::clone(&mock_arc) }; // 执行MyStruct的测试代码 // 测试环境中可以直接unwrap,因为确定是Mock类型 let mut mock_mut = Arc::get_mut(&mut mock_arc).unwrap(); mock_mut.checkpoint(); mock_mut.expect_some_func().return_const(100); // 继续执行后续测试代码 }
如果测试场景中需要多个Arc引用,Arc::get_mut会返回None,这时可以把Mock包在Mutex里:
#[test] fn a_test_with_mutex() { let mock = MockMyTrait::new(); let mock_arc_mutex = Arc::new(Mutex::new(mock)); let testee = MyStruct { data: Arc::clone(&mock_arc_mutex) as Arc<dyn MyTrait> }; // 执行测试代码 // 锁定Mutex获取可变引用 let mut mock_mut = mock_arc_mutex.lock().unwrap(); mock_mut.checkpoint(); mock_mut.expect_some_func().return_const(200); // 继续测试 }
小结
- 能修改MyStruct定义的话,泛型方案是最清晰、类型安全的选择。
- 不能修改结构时,downcast或Mutex包裹Mock都能解决问题,后者适合多线程或多引用的场景。
内容的提问来源于stack exchange,提问作者ptriller
相关产品推荐
相关产品推荐

