-
Notifications
You must be signed in to change notification settings - Fork 77
Open
Description
Summary
Introduce a new mockall API method:
.return_with_context<T>(...), allowing you to define stateful behavior per invocation by passing a mutable context value into each call. This simplifies mocking methods that behave differently over time.
Motivation
In stateful black box testing—such as UI loops, dialogue engines, or retry logic—mocked methods are often called repeatedly, producing different results based on internal state or history. Current patterns using RefCell<VecDeque<T>> are verbose and obscure intent.
Proposed API
impl<T> Expectation<T> {
pub fn return_with_context<C, F>(&mut self, ctx: C, f: F)
where
C: 'static,
F: FnMut(&mut C, Args...) -> Ret + 'static;
}ctxis stored internally and mutated withfon each call.- Provides a clean, localized way to manage state within the mock.
🧪 Example 1: Using usize as a Call Counter
#[automock]
trait Greeter {
fn greet(&self) -> String;
}
let mut mock = MockGreeter::new();
mock.expect_greet()
.return_with_context(0_usize, |count, _| {
*count += 1;
format!("Hello for the {count} time!")
});
assert_eq!(mock.greet(), "Hello for the 1 time!");
assert_eq!(mock.greet(), "Hello for the 2 time!");🧪 Example 2: Using String as Editing Context
#[automock]
trait Editor {
fn edit(&mut self, cmd: &str) -> String;
}
let mut mock = MockEditor::new();
mock.expect_edit()
.return_with_context(String::new(), |text, cmd| {
match *cmd {
"add hello" => text.push_str("hello"),
"add world" => text.push_str(" world"),
"delete" => text.clear(),
_ => (),
}
text.clone()
});
assert_eq!(mock.edit("add hello"), "hello");
assert_eq!(mock.edit("add world"), "hello world");
assert_eq!(mock.edit("delete"), "");Bonus: Possible Sugar Methods
.return_count(...): convenient form ofreturn_with_context(0_usize, ...)where the count is incremented on each call..return_sequence(...): sugar for working with an iterator, where the next value from the iterator is presented on each call.
Why This Matters
- Reduces boilerplate and internal mutability
- Clarifies intent in complex test flows
- Easily adaptable for broader use cases: FSMs, loops, retry logic, interactive systems
Metadata
Metadata
Assignees
Labels
No labels