Giter VIP home page Giter VIP logo

mockall's Introduction

Mockall

A powerful mock object library for Rust.

Build Status Crates.io Documentation

Overview

Mock objects are a powerful technique for unit testing software. A mock object is an object with the same interface as a real object, but whose responses are all manually controlled by test code. They can be used to test the upper and middle layers of an application without instantiating the lower ones, or to inject edge and error cases that would be difficult or impossible to create when using the full stack.

Statically typed languages are inherently more difficult to mock than dynamically typed languages. Since Rust is a statically typed language, previous attempts at creating a mock object library have had mixed results. Mockall incorporates the best elements of previous designs, resulting in it having a rich feature set with a terse and ergonomic interface. Mockall is written in 100% safe and stable Rust.

Usage

Typically mockall is only used by unit tests. To use it this way, add this to your Cargo.toml:

[dev-dependencies]
mockall = "0.12.1"

Then use it like this:

#[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));
    }
}

See the API docs for more information.

Minimum Supported Rust Version (MSRV)

Mockall is supported on Rust 1.71.0 and higher. Mockall's MSRV will not be changed in the future without bumping the major or minor version.

License

mockall is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-APACHE, and LICENSE-MIT for details

Acknowledgements

Mockall was not built in a day. JMock was probably the first popular mock object library. Many ports and imitations have been made, including GoogleMock for C++. Mockers, inspired by GoogleMock, was the first attempt to bring the concept to Rust. The now-defunct Mock_derive was the first library to generate mock objects with procedural macros, greatly reducing the user's workload. Mockall also uses proc macros, and copies many of Mockers' features and conventions. Mockall also takes inspiration from Simulacrum's internal design, and its technique for mocking generic methods.

mockall's People

Contributors

asomers avatar blittable avatar bryanlarsen avatar cardboardturkey avatar dmolokanov avatar enes1313 avatar expyron avatar grimerssy avatar hampusmat avatar humb1t avatar jefftt avatar joshleeb avatar josiahbull avatar larzenegger avatar lucas-schuermann avatar lukaskalbertodt avatar mgeisler avatar monnoroch avatar nbigaouette avatar p3s avatar psiace avatar saikatdas0790 avatar sclaire-1 avatar tompridham avatar vikz95 avatar yancyribbens avatar zhongjn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mockall's Issues

Can't mock a method that returns a reference to a non-Send type

Mockall can mock methods that return non-Send types, but it can't currently mock methods that return references to non-Send types. For example, this won't work:

use mockall::*;

mock! {
    Foo {
        fn baz(&self) -> &std::rc::Rc<u32>;
    }
}
error[E0277]: `std::rc::Rc<u32>` cannot be shared between threads safely
  --> mockall/tests/mock_return_reference.rs:6:1
   |
6  | / mock! {
7  | |     Foo {
8  | |         fn baz(&self) -> &std::rc::Rc<u32>;
9  | |     }
10 | | }
   | |_^ `std::rc::Rc<u32>` cannot be shared between threads safely
   |
   = help: the trait `std::marker::Sync` is not implemented for `std::rc::Rc<u32>`
   = help: see issue #48214
   = help: add #![feature(trivial_bounds)] to the crate attributes to enable

Struct example doesn't work (maybe just for me?)

Hey people, I was trying to run the struct example but it didn't work. Some of the errors were intuitive to fix, but I didn't get too far. Can you guide in making it work? And nice work by the way.

The whole example:

use mockall::*;
use crate::thing::Thing;

mod thing {
    pub struct Thing{}
    #[automock]
    impl Thing {
        pub fn foo(&self) -> u32 {
            return 32;
        }
    }
}

cfg_if! {
    if #[cfg(test)] {
        use self::thing::MockThing as Thing;
    } else {
        use self::thing::Thing;
    }
}

fn do_stuff(thing: &Thing) -> u32 {
    thing.foo()
}

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

    #[test]
    fn test_foo() {
        let mut mock = Thing::default();
        mock.expect_foo().returning(|| 42);
        do_stuff(&mock);
    }
}

The error with rust 1.36:

error[E0658]: The attribute `automock` is currently unknown to the compiler and may have meaning added to it in the future
 --> src/main.rs:6:7
  |
6 |     #[automock]
  |       ^^^^^^^^
  |
  = note: for more information, see https://github.com/rust-lang/rust/issues/29642

error: cannot find macro `cfg_if!` in this scope
  --> src/main.rs:14:1
   |
14 | cfg_if! {
   | ^^^^^^

warning: unused import: `mockall::*`
 --> src/main.rs:1:5
  |
1 | use mockall::*;
  |     ^^^^^^^^^^
  |
  = note: #[warn(unused_imports)] on by default

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.
error: Could not compile `mockall_example`.

To learn more, run the command again with --verbose.

The error with rust 1.39:

error: cannot find macro `cfg_if` in this scope
  --> src/main.rs:14:1
   |
14 | cfg_if! {
   | ^^^^^^

error: cannot find attribute `automock` in this scope
 --> src/main.rs:6:7
  |
6 |     #[automock]
  |       ^^^^^^^^

warning: unused import: `mockall::*`
 --> src/main.rs:1:5
  |
1 | use mockall::*;
  |     ^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error: aborting due to 2 previous errors

error: could not compile `mockall_example`.

To learn more, run the command again with --verbose.

and not necessarily related: I'm doing an evaluation of mocking libraries specific for structs and you're my last hope haha. I tested others with conditional compilation but yours seems more promising and complete. And I'm wondering how it works when the struct is composed of more complex structs. I would love to help more in the crate.

Better error message

Hi,

At the moment with version 0.4 I see error messages like this:

panicked at 'No matching expectation found'

It would be very helpful to see more details, like a name of the expectation.
Last version of 'mockers' library does very well in this regard. Would be great to have the same here.

Allow mocking non-'static structs

Mockall currently can't mock a struct or trait with a lifetime parameter. So something like this doesn't work:

#[allow(unused)]
struct GenericStruct<'a, T, V> {
    t: T,
    v: &'a V
}
#[automock]
impl<'a, T, V> GenericStruct<'a, T, V> {
    #[allow(unused)]
    fn foo(&self, _x: u32) -> i64 {
        42
    }
}

#[test]
fn returning() {
    let mut mock = MockGenericStruct::<'static, u8, i8>::new();
    mock.expect_foo()
        .returning(|x| i64::from(x) + 1);
    assert_eq!(5, mock.foo(4));
}

Question: How to use with functions

Assume I have a simple lib file with several functions
I'd like to mock the read_file and invoke the mocked function when I'm testing write_and_read()
Can this be done with mockall?

use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

fn read_file() -> std::io::Result<String> {
    let file = File::open("foo.txt")?;
    let mut buf_reader = BufReader::new(file);
    let mut contents = String::new();
    buf_reader.read_to_string(&mut contents)?;
    Ok(contents)
}

fn write_file() -> std::io::Result<()> {
    let mut file = File::create("foo.txt")?;
    file.write_all(b"Hello, world!")?;
    Ok(())
}

fn write_and_read() -> std::io::Result<String> {
    write_file()?;
    read_file()
}

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

    #[test]
    fn test_write_and_read() {
        assert_eq!(write_and_read().unwrap(), "Hello, world!");
    }
}

attempt to subtract with overflow when using never

The Expectations::never method doesn't work, even though Expectation::never does. It's a test escape. Trying to use it throws a attempt to subtract with overflow panic instead of the expected panic message. For example:

use mockall::*;

mock!{
    Foo {
        fn foo(&self, x: u32) -> u32;
        fn bar(&self, x: u32);
        fn baz(&self);
    }
}
mod never {
    use super::*;

    #[test]
    #[should_panic(expected = "Expectation should not have been called")]
    fn fail() {
        let mut mock = MockFoo::new();
        mock.expect_bar()
            .returning(|_| ())
            .never();
        mock.bar(0);
    }
}

Eliminate the hack for specializing methods

Now that PR #18 is done, it should no longer be necessary for specializing methods to duplicate the struct's generic parameters in the method signature. However, trying to do that fails, probably because Mockall doesn't realize that a non-generic method with a where clause must use a generic Expectation object.

struct G<T: Copy + Default + 'static>(T);
mock!{
    Foo<T> where T: Copy + 'static {
        fn foo(&self, t: T) -> G<T> where T: Default;
    }
}
error[E0277]: the trait bound `T: std::default::Default` is not satisfied
  --> mockall/tests/mock_specializing_methods.rs:25:10
   |
25 |       Foo<T> where T: Copy + 'static {
   |  __________^
26 | |         fn foo(&self, t: T) -> G<T> where T: Copy + Default + 'static;
   | |______________^ the trait `std::default::Default` is not implemented for `T`

automock can't mock a `new` method with arguments in a trait

If the interface being mocked has a new method, then mockall is not supposed to automatically generate one. That works fine for structs, but apparently it doesn't for traits.

#[automock]
trait Foo {
    #[allow(unused)]
    fn new(_x: u32) -> Self;
}
#[test]
fn return_once() {
    let mock = MockFoo::default();

    MockFoo::expect_new()
        .return_once(|_| mock);

    let _mock = MockFoo::new(5);
}
error[E0061]: this function takes 0 parameters but 1 parameter was supplied
  --> mockall/tests/automock_constructor_in_trait_with_args.rs:21:17
   |
8  | #[automock]
   | ----------- defined here
...
21 |     let _mock = MockFoo::new(5);
   |                 ^^^^^^^^^^^^^^^ expected 0 parameters

Generic methods using `Self` don't work

PR #21 was incomplete. It fixed non-generic methods using Self. However, it broke generic methods using Self. The problem is that mockall_derive/src/mock.rs:397 passes the merged generics field to expectation, but PR #21 treated it as the struct's generic only. The result is that automocking a generic constructor will give an unexpected type argument error.

Allow documentation at `mock` macro

The code produced by the mock! macro cannot be documented right now, but we would like to expose our mocks to the public and provide the documentation for them.
Can you please add support for documentation?

Document usage as dev-depency properly

The readme recommends using mockall as a dev-depency only. However, adapting the following example will fail during a normal build.
This seems like the correct way to fix this:

#[cfg(test)]
use mockall::automock;

#[cfg_attr(test, automock)]
trait MyTrait {
    fn foo(&self, x: u32) -> u32;
}

and should be mentioned in the readme.

return_const doesn't work with methods that return "impl Trait"

As of Mockall 0.4.0, return_const doesn't work with methods that return impl Trait. For example:

pub struct Foo {}

#[automock]
impl Foo {
    fn foo(&self) -> impl Debug + Send { unimplemented!()}
}

#[test]
fn return_const() {
    let mut mock = MockFoo::new();
    mock.expect_foo().return_const(Box::new(4));
    format!("{:?}", mock.foo());
}

will return an error like

the trait `std::convert::From<std::boxed::Box<{integer}>>` is not implemented for `std::boxed::Box<(dyn std::fmt::Debug + std::marker::Send + 'static)>

Trait: default implementation not working?

I want to mock a trait which contains both default implementation of functions and functions that need to be implemented. For example, function_to_mock() needs to be mocked but the default implementation function_with_default_impl() should be used:

trait MyTrait {
    fn function_to_mock(&self);

    fn function_with_default_impl(&self) {
        println!("This is MyTrait::function_with_default_impl()");
        self.function_to_mock();
    }
}

I would expect to be able to do the following to only mock MyTraitfunction_to_mock(): (with mockall version 0.6.0):

use mockall::predicate::*;
use mockall::*;

#[automock]
trait MyTrait {
    fn function_to_mock(&self);

    fn function_with_default_impl(&self) {
        println!("This is MyTrait::function_with_default_impl()");
        self.function_to_mock();
    }
}

struct MyStruct;

impl MyTrait for MyStruct {
    fn function_to_mock(&self) {
        println!("This is MyStruct::function_to_mock()");
    }
}

fn main() {
    println!("---------------------------------------------------");
    let m = MyStruct {};
    m.function_with_default_impl();

    println!("---------------------------------------------------");
    let mut mock = MockMyTrait::new();
    mock.expect_function_to_mock()
        .times(1)
        .returning(|| println!("This is MockMyTrait::function_to_mock()"));
    mock.function_with_default_impl();
}

I would expect the output to be:

---------------------------------------------------
This is MyTrait::function_with_default_impl()
This is MyStruct::function_to_mock()
---------------------------------------------------
This is MyTrait::function_with_default_impl()
This is MockMyTrait::function_to_mock()

but I get a panic instead:

---------------------------------------------------
This is MyTrait::function_with_default_impl()
This is MyStruct::function_to_mock()
---------------------------------------------------
thread 'main' panicked at 'MockMyTrait::function_with_default_impl: No matching expectation found', src/libcore/option.rs:1190:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Is something like that even possible with mockall?

Thanks!

In predicate functions, use deref()ed type for argument types that are Deref

We recently started using mockall and we hit a case where it looks like there's a problem with the code expansion/conversion happening inside the mock! macro. The following code snippet is enough to repro the issue:

trait Foo {
    fn f(input: Vec<u32>);
}

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

    mock! {
        pub Foo {}

        trait Foo {
            fn f(input: Vec<u32>);
        }
    }
}

If you run cargo clippy --tests -- -D clippy::all, then you get the following warning (which we treat as an error) and we have to add an #![allow(clippy::ptr_arg)] to go around for now:

error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
  --> src/main.rs:10:5
   |
10 | /     mock! {
11 | |         pub Foo {}
12 | |
13 | |         trait Foo {
14 | |             fn f(input: Vec<u32>);
15 | |         }
16 | |     }
   | |_____^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg

Trouble resolving super:: in mock methods's signatures

It's perfect valid for super:: to be part of a type in a method's signature. But Mockall has trouble resolving it, probably because of some of the internally created modules. For example, using automock:

use mockall::*;

#[allow(unused)]
mod outer {
    struct SuperT();

    mod inner {
        use super::super::automock;

        pub(crate) struct PubCrateT();
        struct PrivT();

        pub struct Foo();
        #[automock]
        impl Foo {
            pub(crate) fn foo(&self) -> PubCrateT { unimplemented!() }
            fn bar(&self) -> PrivT { unimplemented!() }
            pub(in crate::outer) fn baz(&self) -> super::SuperT {
                unimplemented!()
            }
            pub(in super) fn bang(&self) -> super::SuperT { unimplemented!() }
        }
    }
}

or using mock!

use mockall::*;

#[allow(unused)]
mod outer {
    struct SuperT();

    mod inner {
        use super::super::mock;

        pub(crate) struct PubCrateT();
        struct PrivT();

        mock! {
            Foo {
                fn foo(&self) -> PubCrateT;
                fn bar(&self) -> PrivT;
                fn baz(&self) -> super::SuperT;
                fn bang(&self) -> super::SuperT;
            }
        }
    }
}

These yield compile errors like the following:

error[E0412]: cannot find type `SuperT` in module `super`
  --> mockall/tests/mock_nonpub.rs:21:41
   |
21 |                 fn baz(&self) -> super::SuperT;
   |                                         ^^^^^^ not found in `super`

error[E0412]: cannot find type `SuperT` in module `super`
  --> mockall/tests/mock_nonpub.rs:22:42
   |
22 |                 fn bang(&self) -> super::SuperT;
   |                                          ^^^^^^ not found in `super`

error: aborting due to 2 previous errors

Failed checkpoint assertion should include function name

Continuing a theme of mocking free functions (#30), given a module with more than one function, when a checkpoint() fails, it doesn't include any information about which function in the module didn't meet its requirements.

running 1 test
thread 'test_formatCurrentDir' panicked at 'Expectation called fewer than 1 times', /home/name/.cargo/registry/src/github.com-1ecc6299db9ec823/mockall-0.3.0/src/lib.rs:1333:13

The stacktrace is neither that helpful here, because it points to the line with checkpoint, not the expectation.

Example code, adding temp_dir to a fake std::env:

#![feature(proc_macro_hygiene)]
#![allow(non_snake_case)]

use cfg_if::cfg_if;

cfg_if! {
    if #[cfg(test)] {
        use crate::std::mock_env::current_dir;
    } else {
        use ::std::env::current_dir;
    }
}

#[cfg(test)]
#[allow(dead_code)]
mod std {
    use mockall::automock;

    #[automock]
    pub mod env {
        pub fn current_dir() -> std::io::Result<std::path::PathBuf> {
            Ok("dummy-result".into())
        }

        pub fn temp_dir() -> std::path::PathBuf {
            std::path::PathBuf::new()
        }
    }
}

fn formatCurrentDir() -> String {
    format!("You are in: {:?}", current_dir().unwrap())
}

#[test]
fn test_formatCurrentDir() {
    use crate::std::mock_env::*;
    expect_current_dir().times(1).returning(|| Ok("fake_dir".into()));
    expect_temp_dir().times(1).returning(|| "fake_temp_dir".into());
    assert_eq!("You are in: \"fake_dir\"", formatCurrentDir());
    checkpoint();
}

How to add [automock] conditionally (for example only for tests)

It may seem like a silly question but I've been looking everywhere in the doc for 2 hours and I haven't found it.

I want to add mockall to my dev-dependencies only, so asn't to make the final binary unnecessarily heavy.

For the moment, I'm forced to declare twice each trait to mock....

cfg_if::cfg_if! {
    if #[cfg(test)] {
        #[automock]
        pub(crate) trait BcDbTrait {
            fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError>;
            fn get_current_block(&self) -> Result<Option<DbBlock>, DbError>;
            fn get_db_block_in_local_blockchain(
                &self,
                block_number: BlockNumber,
            ) -> Result<Option<DbBlock>, DbError>;
        }
    } else {
        pub(crate) trait BcDbTrait {
            fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError>;
            fn get_current_block(&self) -> Result<Option<DbBlock>, DbError>;
            fn get_db_block_in_local_blockchain(
                &self,
                block_number: BlockNumber,
            ) -> Result<Option<DbBlock>, DbError>;
        }
    }
}

I tried with a macro but it doesn't work:

macro_rules! automock_test_only {
    ($i:item) => {
        cfg_if::cfg_if! {
            if #[cfg(test)] {
                #[automock]
                $i
            } else {
                $i
            }
        }
    };
}

When I use this marcro I have the following compilation error (rust stable 1.39):

error[E0424]: expected value, found module `self`
  --> lib/modules/gva/src/db.rs:33:17
   |
33 |                   #[automock]
   |                   ^^^^^^^^^^^ `self` value is a keyword only available in methods with `self` parameter
...
42 | / automock_to!(
43 | |     pub(crate) trait BcDbTrait {
44 | |         fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError>;
45 | |         fn get_current_block(&self) -> Result<Option<DbBlock>, DbError>;
...  |
50 | |     }
51 | | );
   | |__- in this macro invocation

error: aborting due to previous error

For more information about this error, try `rustc --explain E0424`.

Failing a static method's checkpoint shouldn't poison that method's mutex

If a mock object hasn't met its expectations, the checkpoint method should panic. Annoyingly, for static methods that method is called with a global mutex held. If it panics, the mutex gets poisoned. That is very inconvenient if multiple tests try to use the same mock class. It should be modified so that the mutex gets released before the panic happens.

Is there a way to specify derives for the generated mock struct?

I have a use case where the trait I'm mocking is always available as a Cloneable concrete type. It looks like there's no option currently to specify custom derives for the mocked struct.

Is this technically feasible to provide? I understand that it would likely be impossible for the cloned copies to contribute to the expectations/assertions/etc but it would allow me to use a unified mock implementation which would be sweet. โค๏ธ

At any rate, thanks for this crate! ๐Ÿ˜„

#![deny(missing_docs)] prevents compilation when using #[automock]

I'm trying to export mocks behind a feature flag in a project with #![deny(missing_docs)] set. My trait and all of its members are documented, but the output of #[automock] causes "missing documentation for" messages:

My example source:

/// Trait for abstracting connections to the database
///
/// A `MockDatabaseAbstraction` struct is exported when the "mockall" feature is used
#[cfg_attr(feature = "mockall", automock)]
pub trait DatabaseAbstraction {
    /// Selects a row for a user login, otherwise `None` is returned
    ///
    /// # Errors
    ///
    /// When a connection can't be obtained from the pool, or the query fails
    fn read_user_login(
        &self,
        user_id: u64,
    ) -> Result<Option<Login>, std::io::Error>;
}

My example output (line numbers are slightly off due to other boilerplate code):

error: missing documentation for a module
  --> src/example.rs:24:33
   |
24 | #[cfg_attr(feature = "mockall", automock)]
   |                                 ^^^^^^^^
   |
note: lint level defined here
  --> src/lib.rs:10:9
   |
10 | #![deny(missing_docs)]
   |         ^^^^^^^^^^^^

error: missing documentation for a struct
  --> src/example.rs:24:33
   |
24 | #[cfg_attr(feature = "mockall", automock)]
   |                                 ^^^^^^^^

error: missing documentation for a module
  --> src/example.rs:24:33
   |
24 | #[cfg_attr(feature = "mockall", automock)]
   |                                 ^^^^^^^^

error: missing documentation for a method
  --> src/example.rs:24:33
   |
24 | #[cfg_attr(feature = "mockall", automock)]
   |                                 ^^^^^^^^

error: aborting due to 4 previous errors

It might be useful to carry over the documentation of the trait itself, and fill in something generic for the generated module. Some IDEs may display useful hints when writing mocked tests

Mocking `spawn` (i.e. for tokio runtime)

With this trait:

pub trait Runtime: Clone + Send + Sync + Unpin + Debug {
    fn spawn<F>(&self, future: F)
    where
        F: Future<Output = ()> + Send + 'static;
}

How do I mock spawn?

I tried like this:

let mock = MockRuntime::new();
mock.expect_spawn().returning(|_| ());
mock
   |
xx |             mock.expect_spawn().returning(|_| ());
   |                                            ^ consider giving this closure parameter a type

Better support for non-Send types

mockall has returning_st method to return non-Send types. However, this capability is not present for other parts of the API:

  • Add return_const_st
  • Add withf_st

This is actually a bit of a pain for me currently as I'm mocking C functions that accept / return pointers. Currently I have to give up on return_const and pass pointers casted with as usize to withf, which is ugly.

Don't work with `&str` as method argument

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

    #[automock]
    pub trait Mailer {
        fn send(&self, email: &str);
    }

    #[test]
    fn name() {
        let mut mock_mailer = MockMailer::new();
        mock_mailer.expect_send().with(predicate::always());
        mock_mailer.send("[email protected]");
    }
}

PrtScn:
mockall_str_error
Compile time error:

the size for values of type `str` cannot be known at compilation time

doesn't have a size known at compile-time

help: the trait `std::marker::Sized` is not implemented for `str`
note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
note: required because of the requirements on the impl of `predicates_core::core::Predicate<str>` for `predicates::constant::BooleanPredicate`rustc(E0277)

I noticed that this error occurs ONLY with never and always predicates.

Allow where clauses on generic methods

Mockall currently works with (most) generic structs that have where clauses, but not generic methods. For example:

mock! {
    Foo {
        fn foo<T>(&self, t: T)
            where T: 'static;
    }
}

#[test]
fn t() {
    let mut mock = MockFoo::new();
    mock::expect_foo::<u32>()
        .returning(());
    mock.foo(42u32);
}

Can't mock method returning &'static str

Hi!
Thanks for the great library!

I have one problem so far.
I'm straggling to mock this method:

fn name(&self) -> &'static str;

It seems like it wants me to supply it with &'static String which is hard to construct (I end up using lazy_static).

project structure / dev. dependency

A question...

The doc shows the crate as a dev-dependency. In a minimal bin this leads to an error if a trait is defined per the doc. If it is a regular dependency, all works well.

Are project mocks somehow defined in integration tests? If so, are the traits duplicated in those integration test crates?

Thanks!

Can't cope with constructor pattern?

Hi, your framework looks very promising, however I'm falling at the first hurdle, which is a trait that implements a new() constructor:

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

    struct Foo {}

    #[automock]
    trait FooTrait {
        fn new() -> Self;
        fn foo(&self) -> String;
    }

    impl FooTrait for Foo {
        fn new() -> Self {
            Foo {}
        }

        fn foo(&self) -> String {
            String::from("I'm SO foo")
        }
    }

    #[test]
    fn test_foo() {
        let mut mock_foo = MockFooTrait::new();
        mock_foo.expect_foo().returning(|| String::from("I'm NOT foo at all"));
        assert_eq!(mock_foo.foo(), "I'm NOT foo at all");
    }
}

Yields the following error:

thread 'tests::test_foo' panicked at 'No matching expectation found', src/libcore/option.rs:1034:5

I've tested it without the constructor and it works perfectly. Am I missing something obvious here or is the framework currently unable to cope with such a constructor?

Question: #[automock] code generation

[fill free to close anytime]
Am I right #[automock] generates bunch of stuff always (both when testing and when building/running/checking)?
in that case how can I add some cfg to prevent code generation (and compilation) when build/run/check?

Can't automock struct when defining own type

I'm recently started using mockall and noticed if a custom result type is used the following code fails to compile:

type Result<T> = std::result::Result<T, String>;

struct MyStruct {}

#[automock]
impl MyStruct {
    fn ret_res() -> Result<String> {
        unimplemented!()
    }
}

cargo build output:

#[automock]
| ^^^^^^^^^^^ unexpected type argument

Allow mocking generic methods with non-'static return values

PR #48 added the ability to mock generic methods whose arguments had lifetime parameters. But it didn't handle the case of return values with lifetime parameters. So the following still doesn't work:

struct X<'a>(&'a u32);

mock!{
    Foo {
        fn foo<'a>() -> X<'a>;
    }
}

deselfify doesn't work with generic types

Methods using Self in their signatures should be mockable because the deselfify routine replaces Self with the mock type. However, it fails to consider type parameters. Hence, the following code doesn't work:

#[automock]
trait Foo<T: 'static> {
    fn new(t: T) -> Self;
}
error[E0107]: wrong number of type arguments: expected 1, found 0
 --> mockall/tests/automock_constructor_in_generic_trait.rs:7:7
  |
7 | trait Foo<T: 'static> {
  |       ^^^ expected 1 type argument

error: aborting due to previous error

Note that such methods can still be mocked using mock! instead of #[automock].

Allow mocking generic constructor methods

Currently Mockall can't mock a constructor method for a generic struct that has bounds on its generic parameters. For example:

mock! {
    pub Foo<T: Default +'static> {
        fn build<T2: Default + 'static>() -> MockFoo<T2>;
    }
}

produces errors like

error[E0277]: the trait bound `T2: std::default::Default` is not satisfied
  --> mockall/tests/mock_generic_constructor_2.rs:7:1
   |
7  | / mock! {
8  | |     pub Foo<T: Default +'static> {
9  | |         fn build<T2: Default + 'static>() -> MockFoo<T2>;
10 | |     }
11 | | }
   | |_^ the trait `std::default::Default` is not implemented for `T2`
   |
   = help: consider adding a `where T2: std::default::Default` bound

Type bound regression

This example doesn't work on master (but it did in 0.5.1):

struct X<T: Debug>(T);

#[automock]
trait Foo {
    fn foo<T: Debug + 'static>(&self, x: X<T>);
}

Gives an error:

`T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`

However if I move type bounds to where it works:

#[automock]
trait Foo {
    fn foo<T>(&self, x: X<T>)
        where T: Debug + 'static;
}

Lift the 'static requirement for return types

Mockall currently requires that the closures passed to methods like returning are 'static. That's overly restrictive. The correct restriction should be that their lifetime is at least as great as the Expectation's. Likewise for the closures passed to methods like withf.

Doesn't work with explicit lifetimes for `&self`

Attempting to mock a method with an explicit lifetime for the &self argument will result in a panic.

mock!{
    Foo {
        fn foo<'life0>(&'life0 self, x: u32) -> u32;
    }
}

results in

error: proc macro panicked
 --> mockall/tests/mock_life0.rs:5:1
  |
5 | / mock!{
6 | |     Foo {
7 | |         fn foo<'life0>(&'life0 self, x: u32) -> u32;
8 | |     }
9 | | }
  | |_^
  |
  = help: message: An unused lifetime parameter?

automock ignores uses statements in module bodies

Great, thank you. That works as is, but extending a little further I'm stuck on the following scoping issue. I can open as a separate issue if that would be better.

some_module.rs:

use cfg_if::cfg_if;
#[cfg(test)]
use mockall::automock;

pub struct SomeStruct {}

#[cfg_attr(test, automock)]
pub mod inner {
    use super::SomeStruct;
    pub fn free_function(_arg: SomeStruct) {}
}

cfg_if! {
    if #[cfg(test)] {
        pub use mock_inner::*;
    } else {
        pub use inner::*;
    }
}

main.rs:

#![feature(proc_macro_hygiene)]
#[cfg(test)]
use mockall::predicate::*;

pub mod some_module;
use some_module::*;

fn my_func() {
    let s = some_module::SomeStruct {};
    some_module::free_function(s);
}

fn main() {
}

#[test]
fn some_test() {
    let context = some_module::free_function_context();
    context.expect().times(1).with(always()).returning(|_x| ());
    my_func();
}

gives:

error[E0412]: cannot find type `SomeStruct` in this scope
  --> src/some_module.rs:10:32
   |
10 |     pub fn free_function(_arg: SomeStruct) {}
   |                                ^^^^^^^^^^ not found in this scope

...

It works if some_module.rs is updated to only uses absolute paths:

#[cfg_attr(test, automock)]
pub mod inner {
    pub fn free_function(_arg: crate::some_module::SomeStruct) {}
}

Originally posted by @lromeo in #49 (comment)

Can't mock Option<&str> without an explicit lifetime name

Can't mock trait with Option<&str> without an explicit lifetime name:

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

    #[automock]
    pub trait Mailer {
        fn send(&self, email: Option<&str>);
    }
}

Error:

error[E0637]: `&` without an explicit lifetime name cannot be used here
  --> modules\user\src\services\entry\usecase.rs:32:38
   |
32 |         fn send(&self, email: Option<&str>);
   |                                      ^ explicit lifetime name needed here

error[E0106]: missing lifetime specifier
  --> modules\user\src\services\entry\usecase.rs:32:38
   |
32 |         fn send(&self, email: Option<&str>);
   |                                      ^ expected lifetime parameter

Public visibility of expect_ methods

One way to work around #75 is to create a mock! and put it inside a struct which implements async traits and forwards calls to the mock. Unfortunately, even when making the contained mock pub, all expect_* methods are private. It would be great to make them public, hence allowing composition of mocks.

Support for anonymous lifetimes

This example doesn't work:

struct X<'a>(&'a u32);

#[automock]
trait Foo {
    fn foo(&self) -> X<'_>;
}
`'_` is a reserved lifetime name

If I specify the lifetime explicitly it does.

Call count for free functions requires manual checkpoint

I wrote a simple example to see if I could mock external modules with free functions. I noticed that times(x) is not checked on expectations for free functions unless I manually added checkpoint().

Here's a snippet for mocking std::env::current_dir:

#![feature(proc_macro_hygiene)]
#![allow(non_snake_case)]

use cfg_if::cfg_if;

cfg_if! {
    if #[cfg(test)] {
        use crate::std::mock_env::current_dir;
    } else {
        use ::std::env::current_dir;
    }
}

mod std {
    use mockall::automock;

    #[automock]
    pub mod env {
        #[allow(dead_code)]
        pub fn current_dir() -> std::io::Result<std::path::PathBuf> {
            Ok("dummy-result".into())
        }
    }
}

fn formatCurrentDir() -> String {
    format!("You are in: {:?}", current_dir().unwrap())
}

#[test]
fn test_formatCurrentDir() {
    use crate::std::mock_env::*;
    // "times(2)" should break the test, but doesn't
    expect_current_dir().times(2).returning(|| Ok("fake_dir".into()));
    assert_eq!("You are in: \"fake_dir\"", formatCurrentDir());
    // checkpoint();  // uncomment to fail the test
}

Is it an intended behavior, have I missed something or is it a bug? If the first, the requirement of using checkpoint() could be added to the docs of modules - https://docs.rs/mockall/0.3.0/mockall/#modules

Can't mock trait containing function that takes an argument of type `Option<&T>`

Seen using mockall 0.5.1 on both stable and nightly.

For example, trying to compile the following fails.

use mockall::automock;

#[automock]
trait Test<T> {
    fn test(value: Option<&T>);
}
error[E0637]: `&` without an explicit lifetime name cannot be used here
 --> src/lib.rs:5:27
  |
5 |     fn test(value: Option<&str>);
  |                           ^ explicit lifetime name needed here

error[E0106]: missing lifetime specifier
 --> src/lib.rs:5:27
  |
5 |     fn test(value: Option<&str>);
  |                           ^ expected lifetime parameter

Workaround

If T is known it's possible to work around this by specifying a lifetime parameter in the method definition, e.g. the following works

#[automock]
trait Test {
    fn test<'a>(value: Option<&'a str>);
}

If the trait is generic this fails with a different error.

#[automock]
trait Test<T> {
    fn test(value: Option<&T>);
}
error[E0261]: use of undeclared lifetime name `'a`
 --> src/lib.rs:5:32
  |
5 |     fn test<'a>(value: Option<&'a T>);
  |                                ^^ undeclared lifetime

error: aborting due to previous error

Add test_double functionality

Simulacrum includes a handy proc macro test_double that is useful for mocking structs. Using test_double instead of cfg_if changes 5 lines into 2 when using a mock struct. test_double can't be used with Mockall as-is because it assumes a different naming convention. But it could easily be reimplemented.

Mocking free functions from modules in another file

Is is possible to mock a module that is defined in another file?

Example
main.rs:

#![feature(proc_macro_hygiene)]
use mockall::automock;
use mockall::predicate::eq;
use cfg_if::cfg_if;

cfg_if! {
    if #[cfg(test)] {
        use mock_some_module::free_function;
    } else {
        use some_module::free_function;
    }
}

#[cfg_attr(test, automock)]
pub mod some_module;

fn my_func() {
    free_function(7);
}

#[test]
fn some_test() {
    let context = mock_some_module::free_function_context();
    context.expect().times(1).with(eq(7)).returning(|_x| ());
    my_func();
}

some_module.rs:

pub fn free_function(_arg: i32) {}

running the tests gives:

 --> src/main.rs:9:13
  |
9 |         use mock_some_module::free_function;
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `free_function` in `mock_some_module`

error[E0425]: cannot find function `free_function_context` in module `mock_some_module`
  --> src/main.rs:30:37
   |
30 |     let context = mock_some_module::free_function_context();
   |                                     ^^^^^^^^^^^^^^^^^^^^^ not found in `mock_some_module

However if I define the module directly in main.rs it works fine:

#[cfg_attr(test, automock)]
#[allow(dead_code)]
mod some_module {
    pub fn free_function(_arg: i32) {}
}

Support for trait's associated const

The following code, when compiled, throws the "missing C in an implementation" error:

use mockall::predicate::*;
use mockall::*;

trait A {
    type T;
    const C: &'static str;

    fn foo(t: Self::T) -> bool;
}

mock! {
    S {}

    trait A {
        type T = isize;
        const C: &'static str = "Mock";

        fn foo(t: isize) -> bool;
    }
}

fn main() {
    println!("Hello, world!");
    let foo_ctx = MockS::foo_context();
    foo_ctx.expect().times(1).return_const(true);
    MockS::foo(0);
    foo_ctx.checkpoint();
}

automock can't handle super in the signature of a free function

mock! and #[automock] create private modules, so they must modify super in any mocked functions' signatures. That works for structs and traits, but currently does not work for free functions, because mockall substitutes the wrong number of supers.

Example:

type T= u32;

#[automock(mod mock_ffi;)]
extern "Rust" {
    #[allow(unused)]
    fn foo(x: super::T) -> i64;
}

Allow where clauses on generic static methods

Mockall doesn't currently allow where clauses on generic static methods, like this:

mock! {
    Foo<T: 'static + Clone> {
        fn new<T2>(t: T2) -> MockFoo<T2> where T2: Clone + 'static;
    }
}

#[test]
fn t() {
    MockFoo::<u32>::expect_new::<u32>()
        .returning(|_| MockFoo::default());
    MockFoo::<u32>::new(42u32);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.