Skip to content

What it is

Mockall generates mock implementations of Rust traits and structs, with expectations on arguments, call counts and return values.

Annotate a trait with #[automock] to get a MockX type. Set expectations with matchers and return values, and assert on the calls made.

Installation

cargo add --dev mockall

Getting started

The smallest useful thing you can do with it, and what each part means.

Mocking a trait
use mockall::{automock, predicate::*};

#[automock]
#[async_trait]
trait BookStore {
    async fn find(&self, id: u32) -> Option<Book>;
    async fn save(&self, book: &Book) -> Result<(), StoreError>;
}

#[tokio::test]
async fn returns_the_book() {
    let mut store = MockBookStore::new();

    store.expect_find()
        .with(eq(42))                 // only matches this argument
        .times(1)                     // and exactly once
        .returning(|_| Some(Book::default()));

    let service = Service::new(store);
    assert!(service.get(42).await.is_some());
    // Expectations are verified when the mock is dropped.
}
Unlike many mocking libraries, verification is automatic on drop — a `times(1)` expectation that never fired fails the test without an explicit assert.

Advanced usage

Where the library earns its place over a simpler alternative.

Sequences and stateful behaviour
let mut seq = mockall::Sequence::new();

store.expect_find()
    .times(1)
    .in_sequence(&mut seq)
    .returning(|_| None);

store.expect_save()
    .times(1)
    .in_sequence(&mut seq)          // must happen after find
    .returning(|_| Ok(()));

// Different responses on successive calls.
let mut count = 0;
store.expect_find().returning(move |_| {
    count += 1;
    if count < 3 { None } else { Some(Book::default()) }
});
Sequences assert ordering, which matters when testing that a cache is checked before the database rather than after.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

No matching expectation found
The call arguments do not match any `with` clause. Loosen the matcher or add the missing expectation — the panic message shows the actual arguments.
automock does not work on a generic trait
Use mock! to declare the mock manually; #[automock] has limits with generics and lifetimes.

Best practices

  • Mock traits you own; mocking a third-party API couples your tests to someone else's design.
  • Prefer a real database via a test container over mocking the data layer — mocks of complex systems drift from reality.
  • Use precise matchers rather than `always`, or the test proves very little.
  • Keep the trait small; a mock with fifteen methods is a design signal.

Background

Why it exists, and what it was reacting to.

Rust's static dispatch makes runtime mocking impossible in the style of dynamic languages. Mockall solves it with macros that generate a mock type at compile time.