如何在注入Rust trait Mock实例时保留其引用以调用Mock方法
解决方案
针对你遇到的问题,这里有三种实用的解决思路:
1. 将Mock专属方法整合到Trait中
如果checkpoint()是WisdomSource这个抽象应该具备的能力(比如测试场景下的通用操作),直接把它加入trait定义。mockall会自动为生成的Mock实现这个方法,这样即使包装成Rc<dyn WisdomSource>,也能直接调用该方法。
示例代码:
use mockall::mock; use std::rc::Rc; // 定义包含checkpoint的Trait trait WisdomSource { fn get_answer(&self) -> String; fn checkpoint(&self); // 新增方法 } // 生成Mock实现 mock! { pub WisdomSource {} impl WisdomSource for WisdomSource { fn get_answer(&self) -> String; fn checkpoint(&self); } } struct Oracle { source: Rc<dyn WisdomSource>, } impl Oracle { fn new(source: Rc<dyn WisdomSource>) -> Self { Oracle { source } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_oracle() { let mut mock = MockWisdomSource::new(); mock.expect_get_answer().returning(|| "test answer".to_string()); mock.expect_checkpoint().once(); let oracle = Oracle::new(Rc::new(mock) as Rc<dyn WisdomSource>); assert_eq!(oracle.source.get_answer(), "test answer"); oracle.source.checkpoint(); // 直接调用 } }
2. 利用Rc的向下转型能力
mockall生成的Mock类型自动实现了Any trait,因此可以通过Rc::downcast将Rc<dyn WisdomSource>转回具体的Rc<MockWisdomSource>,从而调用专属方法。
示例代码:
use mockall::mock; use std::any::Any; use std::rc::Rc; // 让Trait继承Any,支持向下转型 trait WisdomSource: Any { fn get_answer(&self) -> String; } mock! { pub WisdomSource {} impl WisdomSource for WisdomSource { fn get_answer(&self) -> String; } } struct Oracle { source: Rc<dyn WisdomSource>, } impl Oracle { fn new(source: Rc<dyn WisdomSource>) -> Self { Oracle { source } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_oracle() { let mut mock = MockWisdomSource::new(); mock.expect_get_answer().returning(|| "test answer".to_string()); let rc_mock = Rc::new(mock) as Rc<dyn WisdomSource>; let oracle = Oracle::new(Rc::clone(&rc_mock)); // 向下转型到具体Mock类型,失败时会panic,测试环境可接受 let mock_ref = rc_mock.downcast::<MockWisdomSource>().unwrap(); mock_ref.checkpoint(); // 调用Mock专属方法 assert_eq!(oracle.source.get_answer(), "test answer"); } }
3. 单独持有Mock实例的引用
在测试代码中,同时保留原始Mock实例的Rc引用,将克隆后的Rc包装成trait对象注入Oracle。这样无需转型,直接用原始引用调用Mock专属方法。
示例代码:
use mockall::mock; use std::rc::Rc; trait WisdomSource { fn get_answer(&self) -> String; } mock! { pub WisdomSource {} impl WisdomSource for WisdomSource { fn get_answer(&self) -> String; } } struct Oracle { source: Rc<dyn WisdomSource>, } impl Oracle { fn new(source: Rc<dyn WisdomSource>) -> Self { Oracle { source } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_oracle() { let mut mock = MockWisdomSource::new(); mock.expect_get_answer().returning(|| "test answer".to_string()); let rc_mock = Rc::new(mock); // 克隆Rc注入Oracle,保留原始rc_mock的所有权 let oracle = Oracle::new(Rc::clone(&rc_mock) as Rc<dyn WisdomSource>); rc_mock.checkpoint(); // 直接调用Mock专属方法 assert_eq!(oracle.source.get_answer(), "test answer"); } }
这种方式最简单直接,适合不需要在Oracle内部触发checkpoint,仅在测试逻辑中手动调用的场景。
内容的提问来源于stack exchange,提问作者Heikki Taavettila
相关产品推荐
相关产品推荐

