mockall

repository·master·Indexed 23 days ago

https://github.com/asomers/mockall

A powerful mock object library for Rust (v0.15.0) that allows developers to create objects with the same interface as real objects but with manually controlled responses for unit testing. It provides the #[automock] attribute for automatic mock generation of traits and structs, a mock! macro for manual mock definition, and a comprehensive expectation API to control return values, call counts, and call order using Sequence. Supports async traits and generic methods via #[mockall::concretize]. Requires Rust 1.77.0 or higher.

Tokens
3.9K
Snippets
14
Records
17
Agent score
83%

What's inside mockall

  1. Enforce call order with `Sequence`

    master

    To ensure that multiple expectations are called in a specific order, use a Sequence object. Any expectation can be added to the same sequence using .in_sequence(&mut sequence).

    use mockall::{automock, Sequence};
    
    #[automock]
    trait Foo {
        fn foo(&self);
    }
    
    let mut seq = Sequence::new();
    let mut mock1 = MockFoo::new();
    let mut mock2 = MockFoo::new();
    
    mock1.expect_foo()
        .times(1)
        .in_sequence(&mut seq)
        .returning(|| ());
    
    mock2.expect_foo()
        .times(1)
        .in_sequence(&mut seq)
        .returning(|| ());
    
    // mock1.foo() MUST be called before mock2.foo(), otherwise it panics.
    use mockall::{automock, Sequence};
    
    #[automock]
    trait Foo {
        fn foo(&self);
    }
    
    let mut seq = Sequence::new();
    let mut mock1 = MockFoo::new();
    let mut mock2 = MockFoo::new();
    
    mock1.expect_foo()
        .times(1)
        .in_sequence(&mut seq)
        .returning(|| ());
    
    mock2.expect_foo()
        .times(1)
        .in_sequence(&mut seq)
        .returning(|| ());
  2. Enforce call order using `Sequence`

    master

    Use Sequence to ensure that mock calls occur in a specific, predefined order. Sequences are performed greedily: they will always try to match the earliest possible element in the sequence that is currently allowed to be called.

    How it works

    1. Create a new sequence: let mut seq = Sequence::new();.
    2. Attach expectations to the sequence using .in_sequence(&mut seq).
    3. The sequence progresses as expectations are satisfied. An expectation is satisfied when its minimum call count is met.

    Important Behaviors

    • Greedy Matching: If you have two expectations for the same method in a sequence, and the first one allows an arbitrary number of calls (e.g., .times(1..)), the first expectation will consume all calls, and the second expectation will never be reached, causing a test failure.
    • Call Count Variation: The number of calls to an individual method can vary, provided the previous method in the sequence has met its minimum required calls before the next method is invoked.
    # use mockall::*; 
    #[automock]
    trait Foo {
        fn foo(&self);
        fn bar(&self) -> u32;
    }
    let mut seq = Sequence::new();
    
    let mut mock0 = MockFoo::new();
    let mut mock1 = MockFoo::new();
    
    mock0.expect_foo()
        .times(1)
        .returning(|| ())
        .in_sequence(&mut seq);
    
    mock1.expect_bar()
        .times(1)
        .returning(|| 42)
        .in_sequence(&mut seq);
    
    mock0.foo();
    mock1.bar();
  3. Quickstart: Mocking a trait with #[automock]

    master

    The easiest way to use Mockall is the #[automock] attribute. It generates a mock struct with the same name as your trait, prepended with Mock. You can then instantiate it using Mock<TraitName>::new(), set expectations on its methods, and provide it to your code.

    use mockall::automock;
    use mockall::predicate;
    
    #[automock]
    trait MyTrait {
        fn foo(&self, x: u32) -> u32;
    }
    
    fn call_with_four(x: &dyn MyTrait) -> u32 {
        x.foo(4)
    }
    
    #[test]
    fn test_foo() {
        let mut mock = MockMyTrait::new();
        mock.expect_foo()
            .with(predicate::eq(4))
            .times(1)
            .returning(|x| x + 1);
        assert_eq!(5, call_with_four(&mock));
    }
    use mockall::automock;
    use mockall::predicate;
    
    #[automock]
    trait MyTrait {
        fn foo(&self, x: u32) -> u32;
    }
    
    fn call_with_four(x: &dyn MyTrait) -> u32 {
        x.foo(4)
    }
    
    #[test]
    fn test_foo() {
        let mut mock = MockMyTrait::new();
        mock.expect_foo()
            .with(predicate::eq(4))
            .times(1)
            .returning(|x| x + 1);
        assert_eq!(5, call_with_four(&mock));
    }
  4. Use `#[mockall::concretize]` for generic methods with non-`'static` parameters

    master

    By default, Mockall requires generic arguments to be 'static. To allow non-'static generic parameters, decorate the method with #[mockall::concretize]. This tells Mockall to treat generic arguments as trait objects.

    Important Constraints

    • Import Requirement: You MUST import the attribute with its canonical name #[mockall::concretize]. Aliasing it will cause it to fail.
    • Expectation Matching: Concretized methods can only be matched using .withf or .withf_st, not .with.
    • Shared Expectations: Generic methods will share expectations across all argument types; you cannot specify different expectations for expect_foo::<i32> and expect_foo::<u64>.
    • Supported Patterns: Currently supports T, &T, &mut T, and &[T] as generic parameters.
    # use std::path::Path;
    # use mockall::{automock, concretize};
    #[automock]
    trait Foo {
        #[mockall::concretize]
        fn foo<P: AsRef<Path>>(&self, p: P);
    }
    
    # fn main() {
    let mut mock = MockFoo::new();
    mock.expect_foo()
        .withf(|p| p.as_ref() == Path::new("/tmp"))
        .return_const(());
    mock.foo(Path::new("/tmp"));
    # }
  5. Manually mock structures with `mock!`

    master

    When #[automock] is insufficient (e.g., mocking foreign types, structs with multiple impl blocks, or choosing a custom mock name), use the mock! macro. You must essentially redefine the structure and its implementations without bodies.

    Syntax Format

    1. Optional visibility specifier.
    2. The real structure name and its generic fields.
    3. A {} block containing method signatures (without bodies).
    4. Zero or more impl blocks for traits (without bodies).

    Key Patterns

    • Generics: When mocking a generic struct's implementation of a generic trait, use the same name for their generic parameters (e.g., Rc<T> implementing AsRef<T>).
    • Associated Types: Specify concrete types directly in the mock!{} block (e.g., type Item=u32;).
    # use mockall_derive::mock;
    trait Foo {
        fn foo(&self, x: u32);
    }
    
    mock!{
        pub MyStruct<T: Clone + 'static> {
            fn bar(&self) -> u8;
        }
        impl<T: Clone + 'static> Foo for MyStruct<T> {
            fn foo(&self, x: u32);
        }
    }
  6. Use `#[automock]` to automatically generate mock types

    master

    The #[automock] attribute is the easiest way to use Mockall. It works on most traits and structs with a single impl block. It generates a mock struct named Mock<Name> (e.g., MockFoo for Foo). For every method in the original, the mock struct provides an expect_<method_name> method to set expectations.

    Supported Scenarios

    • Traits: Generates a mock struct for the trait.
    • Structs: Generates a mock struct for the struct's methods.
    • Trait implementations on structs: Generates a mock for the specific implementation.
    • Modules: Generates a mock module named mock_<module_name> containing the functions.
    • FFI/Foreign functions: Mocks foreign function modules.

    Advanced #[automock] Configuration

    • Targeting a specific trait variant: Use #[automock(target = TraitName)] if you are using trait_variant and want to mock a specific variant instead of the local trait.
    • Associated types: Specify types using #[automock(type Item=u32;)].

    Limitations

    #[automock] cannot be used for:

    • Structs with multiple impl blocks.
    • Structs or traits defined in other crates.
    • Traits with trait bounds.
    • When you need a custom name for the generated mock struct.
    # use mockall_derive::automock;
    #[automock]
    pub trait Foo {
        fn foo(&self, key: i16);
    }
    
    let mock = MockFoo::new();
  7. Mocking Async Traits

    master

    Mockall supports async traits (including those using the async_trait or trait_variant crates).

    Important Requirements:

    1. The #[automock] attribute must appear before the crate's async attribute.
    2. When mocking methods that return impl Future, you must use Box::pin in your expectations.
    use async_trait::async_trait;
    use mockall::automock;
    use futures::{Future, future};
    
    #[automock]
    #[async_trait]
    pub trait Foo {
       async fn foo(&self) -> u32;
    }
    
    // For methods returning impl Future:
    #[automock]
    impl Foo {
        fn foo(&self) -> impl Future<Output=i32> {
            // ...
            # future::ready(42)
        }
    }
    
    let mut mock = MockFoo::new();
    mock.expect_foo()
        .returning(|| Box::pin(future::ready(42)));
    use async_trait::async_trait;
    use mockall::automock;
    use futures::{Future, future};
    
    #[automock]
    #[async_trait]
    pub trait Foo {
       async fn foo(&self) -> u32;
    }
    
    // For methods returning impl Future:
    #[automock]
    impl Foo {
        fn foo(&self) -> impl Future<Output=i32> {
            // ...
            # future::ready(42)
        }
    }
    
    let mut mock = MockFoo::new();
    mock.expect_foo()
        .returning(|| Box::pin(future::ready(42)));
  8. Create mocks using the automock macro

    master

    The easiest way to create a mock for a trait is to use the #[cfg_attr(test, automock)] attribute. This ensures the mock implementation is only generated during testing. When applied to a trait named MyTrait, it generates a struct named MockMyTrait.

    #[cfg(test)]
    use mockall::{automock, mock, predicate::*};
    
    #[cfg_attr(test, automock)]
    trait MyTrait {
        fn foo(&self, x: u32) -> u32;
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn mytest() {
            let mut mock = MockMyTrait::new();
            mock.expect_foo()
                .with(eq(4))
                .times(1)
                .returning(|x| x + 1);
            assert_eq!(5, mock.foo(4));
        }
    }
  9. Configure mock expectations

    master

    Once a mock object is instantiated (e.g., MockMyTrait::new()), you can define its behavior using an expectation API:

    • .expect_<method_name>(): Starts an expectation for a specific method.
    • .with(...): Defines arguments that the method must be called with. Uses predicates like eq() from mockall::predicate.
    • .times(n): Specifies exactly how many times the method should be called.
    • .returning(|args| ...): Defines the closure that provides the return value for the method call.