Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow mocking some methods with generic non-static arguments #408

Merged
merged 1 commit into from
Sep 20, 2022

Conversation

asomers
Copy link
Owner

@asomers asomers commented Sep 20, 2022

Add a #[mockall::concretize] attribute. When set on a function or method, its generic expectations will be turned into trait objects.

But it only works for function arguments that are pure generic types or a few basic combinations:

  • T
  • &T
  • &mut T
  • &[T]

Issue #217

Add a #[mockall::concretize] attribute.  When set on a function or
method, its generic expectations will be turned into trait objects.

But it only works for function arguments that are pure generic types or
a few basic combinations:
* T
* &T
* &mut T
* &[T]

Issue #217
@cjriches
Copy link

This seems great and I'd love to use it, but unless I'm doing something very wrong, it doesn't seem to work if mockall is a dev-dependency.

Specifically, the problem is that #[mockall::concretize] only seems to take effect when used directly, and #[cfg_attr(test, mockall::concretize)] has no effect. This means that mockall must be present in non-test builds, which is far from ideal.

I'm guessing this is something to do with the following line in the docs, suggesting that the attribute is more of a textual marker than taking effect directly:

NB: This attribute must be imported with its canonical name.  It won't work otherwise!

Is this fixable?

@asomers
Copy link
Owner Author

asomers commented Nov 13, 2022

Hm, you must be doing something wrong, because I use concretize with mockall as a dev-dependency. The warning you mention isn't related. It just means, don't do something like import mockall::concretize as something_else.

@cjriches
Copy link

Let me provide an example, and hopefully you can tell me what I'm doing wrong.

The following is based on the example in the docstring:

use std::path::Path;

#[cfg(test)]
use mockall::{automock, concretize};

#[cfg_attr(test, automock)]
trait Foo {
    #[concretize]
    fn foo<P: AsRef<Path>>(&self, p: P);
}

fn main() {}

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

    #[test]
    fn some_test() {
        let mut mock = MockFoo::new();
        mock.expect_foo()
            .withf(|p| p.as_ref() == Path::new("/tmp"))
            .return_const(());
        mock.foo(Path::new("/tmp"));
    }
}

This works fine in test mode, but cargo check errors out because #[concretize] isn't defined. If I simply change #[concretize] to #[cfg_attr(test, concretize)], then it compiles fine in normal mode, but the following errors spew out in test mode:

error[E0658]: attributes on expressions are experimental                                                                             [0/464] --> src/main.rs:8:22
  |
8 |     #[cfg_attr(test, concretize)]
  |                      ^^^^^^^^^^
  |
  = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information

error: expected non-macro attribute, found attribute macro `concretize`
 --> src/main.rs:8:22
  |
8 |     #[cfg_attr(test, concretize)]
  |                      ^^^^^^^^^^ not a non-macro attribute

error[E0658]: custom attributes cannot be applied to expressions
 --> src/main.rs:8:22
  |
8 |     #[cfg_attr(test, concretize)]
  |                      ^^^^^^^^^^
  |
  = note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information

error[E0310]: the parameter type `P` may not live long enough
    --> src/main.rs:6:18
     |
6    | #[cfg_attr(test, automock)]
     |                  ^^^^^^^^ ...so that the type `Expectations<P>` will meet its required lifetime bounds...
     |
note: ...that is required by this bound
    --> /home/cjriches/.cargo/git/checkouts/mockall-17cf93ce910ebbb5/b51cb30/mockall/src/lib.rs:1418:29
     |
1418 | pub trait AnyExpectations : Any + Send + Sync {}
     |                             ^^^
     = note: this error originates in the attribute macro `automock` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider adding an explicit lifetime bound...
     |
9    |     fn foo<P: AsRef<Path>> + 'static(&self, p: P);
     |                            +++++++++

error[E0310]: the parameter type `P` may not live long enough
    --> src/main.rs:6:18
     |
6    | #[cfg_attr(test, automock)]
     |                  ^^^^^^^^ ...so that the type `P` will meet its required lifetime bounds...
     |
note: ...that is required by this bound
    --> /home/cjriches/.cargo/git/checkouts/mockall-17cf93ce910ebbb5/b51cb30/mockall/src/lib.rs:1418:29
     |
1418 | pub trait AnyExpectations : Any + Send + Sync {}
     |                             ^^^
     = note: this error originates in the attribute macro `automock` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider adding an explicit lifetime bound...
     |
9    |     fn foo<P: AsRef<Path>> + 'static(&self, p: P);
     |                            +++++++++

The last two errors seem to just be the effects of concretize not happening, but the first few are more worrying.

How do I get concretize to compile under both normal and test mode?

@asomers
Copy link
Owner Author

asomers commented Nov 14, 2022

Yep, it's a bug. I never noticed because I've only used concretize with mock!, not #[automock]. Moving discussion to #427 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants