Introduction

Mockall is the standard mocking library for Rust, generating mock implementations of traits for unit testing. However, it has specific limitations around generic methods, async traits, and complex return types. When mock expectations are not properly configured, tests panic with No matching expectation found or fail to compile due to unsupported trait features. Understanding Mockall's capabilities and limitations is essential for effective test-driven development in Rust.

Mockall works by generating mock struct implementations at compile time using procedural macros. The #[automock] attribute generates a mock version of the trait, creating a MockTraitName struct with methods like expect_method_name(). These expectation methods allow you to configure what the mock should return when called with specific arguments. The key is understanding that expectations must be set before the mock is used, and the mock validates that the actual calls match the configured expectations.

Symptoms

  • MockXXX::method(#X): No matching expectation found panic at runtime
  • the method 'returning' exists but its trait bounds were not satisfied compilation error
  • Cannot mock generic methods with type parameters
  • Async trait methods not mocked correctly with mockall
  • Mock expectations called in wrong order causing test failure
  • Tests pass locally but fail in CI due to mock state leaking between tests
  • MockXXX::method(#X): Expectation had fewer calls than expected panic on test completion

Error output: `` thread 'tests::test_process' panicked at 'MockDatabaseClient::query(#1): No matching expectation found'

Common Causes

  • Mock expectation not set before calling the method
  • Generic method requires mock! macro instead of #[automock]
  • Return value does not match method return type
  • Async trait requires special handling with mockall
  • Multiple calls but only one expectation configured

Step-by-Step Fix

  1. 1.Basic mock setup with proper expectations:
  2. 2.```rust
  3. 3.use mockall::automock;

#[automock] trait UserRepository { fn find_by_id(&self, id: u32) -> Option<User>; fn save(&self, user: User) -> Result<User, DbError>; }

#[cfg(test)] mod tests { use super::*; use mockall::predicate::*;

#[test] fn test_find_user() { let mut mock = MockUserRepository::new();

// Set expectation BEFORE calling the method mock.expect_find_by_id() .with(eq(42)) .times(1) .returning(|id| Some(User { id, name: "Test".to_string() }));

// Now call the method let result = mock.find_by_id(42); assert!(result.is_some()); assert_eq!(result.unwrap().name, "Test"); }

#[test] #[should_panic(expected = "No matching expectation found")] fn test_unexpected_call() { let mock = MockUserRepository::new(); // No expectation set - will panic mock.find_by_id(42); } } ```

  1. 1.Mock generic methods with mock! macro:
  2. 2.```rust
  3. 3.use mockall::mock;

// automock cannot handle generic methods // #[automock] // FAILS for generic methods trait DataStore { fn get<T: DeserializeOwned>(&self, key: &str) -> Result<T>; }

// Use mock! macro instead mock! { pub DataStore {}

impl DataStore for DataStore { fn get<T: DeserializeOwned + 'static>(&self, key: &str) -> Result<T>; } }

#[cfg(test)] mod tests { use super::*;

#[test] fn test_get_string() { let mut mock = MockDataStore::new(); mock.expect_get::<String>() .with(eq("key")) .returning(|_| Ok("value".to_string()));

let result: String = mock.get("key").unwrap(); assert_eq!(result, "value"); } } ```

  1. 1.Mock async trait methods:
  2. 2.```rust
  3. 3.use async_trait::async_trait;
  4. 4.use mockall::automock;

#[automock] #[async_trait] trait AsyncService { async fn fetch_data(&self, id: u32) -> Result<String, Error>; }

#[cfg(test)] mod tests { use super::*;

#[tokio::test] async fn test_async_fetch() { let mut mock = MockAsyncService::new();

mock.expect_fetch_data() .with(eq(1)) .returning(|_| Box::pin(async { Ok("fetched data".to_string()) }));

let result = mock.fetch_data(1).await; assert_eq!(result.unwrap(), "fetched data"); } } ```

  1. 1.Configure multiple expectations and call ordering:
  2. 2.```rust
  3. 3.#[test]
  4. 4.fn test_multiple_calls() {
  5. 5.let mut mock = MockUserRepository::new();

// Multiple calls to same method with different args mock.expect_find_by_id() .with(eq(1)) .times(1) .returning(|_| Some(User { id: 1, name: "Alice".into() }));

mock.expect_find_by_id() .with(eq(2)) .times(1) .returning(|_| Some(User { id: 2, name: "Bob".into() }));

// Sequence: specific order required let mut seq = mockall::Sequence::new();

mock.expect_save() .times(1) .in_sequence(&mut seq) .returning(|user| Ok(user));

mock.expect_find_by_id() .with(eq(1)) .times(1) .in_sequence(&mut seq) .returning(|_| Some(User { id: 1, name: "Alice".into() }));

// Calls must be in sequence order mock.save(User { id: 1, name: "Alice".into() }).unwrap(); mock.find_by_id(1); } ```

Prevention

  • Always set expectations before calling mock methods
  • Use .times(n) to verify exact call counts
  • Use mock! macro for generic methods, #[automock] for simple traits
  • Use return_const for simple return values, returning for computed values
  • Add .times(0) to assert a method was NOT called
  • Use Checkpoint to clear expectations between test cases
  • Use #[cfg(test)] to ensure mock code only compiles in test mode
  • Create helper functions to reduce boilerplate in test setup
  • Use predicates like always(), eq(), and function() for flexible matching
  • Document mock expectations in test function names or comments

Verification

Test that mocks are configured correctly:

```rust #[cfg(test)] mod tests { use super::*; use mockall::predicate::*;

#[test] fn test_mock_expectations_met() { let mut mock = MockUserRepository::new();

// Set up expectations mock.expect_find_by_id() .with(eq(42)) .times(1) .returning(|_| Some(User { id: 42, name: "Test".into() }));

// Use the mock let result = mock.find_by_id(42); assert!(result.is_some());

// Mockall automatically verifies all expectations were met // when the mock is dropped }

#[test] #[should_panic(expected = "Expectation had fewer calls")] fn test_unmet_expectation_panics() { let mut mock = MockUserRepository::new();

mock.expect_find_by_id() .times(1) // Expect 1 call .returning(|_| None);

// Never call find_by_id - test will panic on drop }

#[test] fn test_checkpoint_clears_expectations() { let mut mock = MockUserRepository::new();

mock.expect_find_by_id() .returning(|_| None);

mock.find_by_id(1); mock.checkpoint(); // Verify and clear expectations

// Set new expectations for next phase mock.expect_find_by_id() .returning(|_| Some(User { id: 99, name: "New".into() }));

let result = mock.find_by_id(1); assert!(result.is_some()); } } ```

Run tests with verbose output:

```bash cargo test -- --nocapture

# Run specific test cargo test test_find_user -- --nocapture

# Run with mockall debugging RUST_LOG=mockall=debug cargo test ```

  • [WordPress troubleshooting: Fix IAM Permission Denied - Complete Tro](fix-iam-permission-denied-ygxm)
  • [Technical troubleshooting: Fix Borrow Checker E0502 Mutable Immutable Borrow ](borrow-checker-e0502-mutable-immutable-borrow)
  • [Technical troubleshooting: Fix Build Rs Environment Variable Not Rerun Issue ](build-rs-environment-variable-not-rerun)
  • [Technical troubleshooting: Fix Clippy Dead Code Pub Fn Binary Crate Issue in ](clippy-dead-code-pub-fn-binary-crate)
  • [Fix Crossbeam Send Disconnected Channel Panic Issue in Rust](crossbeam-send-disconnected-channel-panic)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Fix Rust Mockall Mocking Trait Test Double Configuration", "description": "Fix Rust Mockall mocking errors for trait test doubles. Covers generic methods, return value configuration, expectation ordering, and async trait mocking.", "url": "https://www.fixwikihub.com/rust-mockall-mocking-trait-test-double", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-07T22:01:08.675Z", "dateModified": "2026-01-07T22:01:08.675Z" } </script>