Multiple calls
The problem
Methods on your mock object might be invoked multiple times by the code under test.
In more complex scenarios, you might need to return different values for each invocation. Let's see how to do that.
times
When you add a times
expectation to a method, mockall
will use the return value you specified
up until the times
-th invocation.
#![allow(unused)] fn main() { #[test] fn test_times() { let mut mock = MockEmailSender::new(); let email = /* */; mock.expect_send() .times(2) .returning(|_| Ok(())); mock.send(&email); mock.send(&email); // This panics! mock.send(&email); } }
You can leverage this feature to return different values depending on the number of times a method has been called.
#![allow(unused)] fn main() { #[test] fn test_times() { let mut mock = MockEmailSender::new(); let email = /* */; mock.expect_send() .times(1) .returning(|_| Ok(())); mock.expect_send() .times(1) .returning(|_| Err(/* */)); // This returns Ok(())... mock.send(&email); // ...while this returns Err! mock.send(&email); } }
Sequence
What we have seen so far works well when you need to return different values for different invocations of the same
method.
You can take this one step further by defining a sequence of calls that your mock object should expect, spanning
multiple methods.
#![allow(unused)] fn main() { #[test] fn test_sequence() { let mut mock = MockEmailSender::new(); let email = /* */; let mut sequence = Sequence::new(); mock.expect_send() .times(1) .in_sequence(&mut sequence) .returning(|_| Ok(())); mock.expect_get_inbox() .times(1) .in_sequence(&mut sequence) .returning(|_| Ok(/* */)); // This panics because the sequence expected `send` to be called first! mock.get_inbox(); } }
When using a Sequence
, you need to make sure that the methods are invoked in the order specified by the sequence.
Invoking get_inbox
before send
will cause the test to fail, even if they are both called exactly once.
Exercise
The exercise for this section is located in 03_mocks/03_sequences