模拟返回 Option 的特征方法<&T>导致生命周期冲突

问题描述 投票:0回答:2

我正在尝试模拟一个特征进行测试,但 ATM 无法实现功能。
我使用板条箱mockall

错误消息是:

cannot infer an appropriate lifetime due to conflicting requirements
but, the lifetime must be valid for the static lifetime...
expected `&mut __get_mapping::Expectation<'_>`
   found `&mut __get_mapping::Expectation<'static>`

我知道问题是一生的冲突。 但是传递给闭包的项目已经具有匿名生命周期,即使我没有明确声明。

我试图找到一些带有解决方案或提示的示例...... 但我找不到解决方案。

有没有办法指定返回项具有除静态之外的生命周期? 我也尝试指定闭包的返回类型的生命周期,但错误仍然是相同的。

到目前为止我的代码是:

#[mockall::automock]
pub trait Analyzer {
    //...
    fn get_mapping<'a>(&'a self, old: &Item) -> Option<&'a Item>;
    //...
}



fn test() {
    let mut analyzer = MockAnalyzer::new();
    analyzer.expect_get_mapping().returning(|item:&'_ Item| Some(item));
    // also tried ... returning(|item:&'_ Item| -> Option<&'_ Item> {Some(item)})
    // my initial code was ... returning(|item| Some(item))
    //...
}

rust mocking lifetime mockall
2个回答
1
投票

如果您的

Item
Clone
,您可能会遇到以下情况:

analyzer
    .expect_get_mapping()
    .returning(|item: &'_ Item| Some(Box::leak(Box::new(item.clone()))));

这确实会泄漏堆内存,所以如果你的测试代码执行超过几千万次,可能会造成麻烦。


0
投票

可以使用

mockall
提供的模拟来实现具有非
'static
生命周期的特征,以便通过手动为模拟构建
impl
来调用直接作为模拟一部分的模拟函数。 虽然参数的生命周期没有任何实际限制,但返回值通常必须具有
'static
生命周期,根据 对 GitHub 问题 asomers/mockall#362
 的响应。  对于下面的第一个演示,我假设 
Item
 是可以轻松制作的东西 
static

use mockall::mock; use mockall::predicate::eq; #[derive(Debug, PartialEq)] pub struct Item(&'static str); pub trait Analyzer { fn get_mapping<'a>(&'a self, old: &Item) -> Option<&'a Item>; } mock! { pub Analyzer { // the mocked inner function for the mock fn mocked_inner_function<'a>(&self, old: &'a Item) -> Option<&'static Item>; } } impl Analyzer for MockAnalyzer { fn get_mapping<'a>(&'a self, old: &Item) -> Option<&'a Item> { // the impl just calls the mocked inner function self.mocked_inner_function(old) } }

(当然,请用与目标方法更匹配的名称来命名mocked_inner_function

,这里这样命名是为了清楚地定义事物的位置。)

现在进行测试本身:

// the static return value static RESULT: Item = Item("result"); #[test] fn test_analyzer_success() { let mut mock_analyzer = MockAnalyzer::new(); mock_analyzer .expect_mocked_inner_function() .times(1) .with(eq(Item("old"))) .return_const(Some(&RESULT)); // test the mock assert_eq!(mock_analyzer.get_mapping(&Item("old")), Some(&RESULT)); } #[test] fn test_analyzer_failure() { let mut mock_analyzer = MockAnalyzer::new(); mock_analyzer .expect_mocked_inner_function() .times(2) // expected to be called twice... .with(eq(Item("old"))) .return_const(Some(&RESULT)); // ... but only called once. assert_eq!(mock_analyzer.get_mapping(&Item("old")), Some(&RESULT)); }
第一个测试应该通过,第二个测试应该失败。  现在,如果返回值是一个更复杂的值,无法轻松静态构造,则使用根据每个预期测试返回值专门定义的 

OnceLock<Item>

 可能是一个可行的替代方案:

use std::sync::OnceLock; static TEST_ANOTHER_TRIAL: OnceLock<Item> = OnceLock::new(); #[test] fn test_another_trial() { TEST_ANOTHER_TRIAL.set(Item("another return value")) .expect("this test result value was used elsewhere!"); let mut mock_analyzer = MockAnalyzer::new(); mock_analyzer .expect_mocked_inner_function() .times(1) .with(eq(Item("old"))) .returning(|_| Some(TEST_ANOTHER_TRIAL.get() .expect("the return value was not already set"))); assert_eq!( mock_analyzer.get_mapping(&Item("old")), Some(&Item("another return value")) ); }
虽然 

lazy_static!

 可以用作 
OnceLock
 的传统替代品,但它不太惯用,并且不能保证静态值没有在另一个测试函数中重用。

© www.soinside.com 2019 - 2024. All rights reserved.