我正在尝试模拟一个特征进行测试,但 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))
//...
}
如果您的
Item
是 Clone
,您可能会遇到以下情况:
analyzer
.expect_get_mapping()
.returning(|item: &'_ Item| Some(Box::leak(Box::new(item.clone()))));
这确实会泄漏堆内存,所以如果你的测试代码执行超过几千万次,可能会造成麻烦。
可以使用
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));
}
第一个测试应该通过,第二个测试应该失败。 现在,如果返回值是一个更复杂的值,无法轻松静态构造,则使用根据每个预期测试返回值专门定义的 可能是一个可行的替代方案:
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
的传统替代品,但它不太惯用,并且不能保证静态值没有在另一个测试函数中重用。