-
Notifications
You must be signed in to change notification settings - Fork 78
Provide clear_* and clear_and_expect_* mock methods to enable clearing the expectations array for automock #676
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| //! automocking a struct | ||
| #![deny(warnings)] | ||
|
|
||
| use std::panic; | ||
| use mockall::*; | ||
|
|
||
| pub struct SimpleStruct {} | ||
|
|
@@ -20,3 +21,47 @@ fn returning() { | |
| .returning(|x| i64::from(x) + 1); | ||
| assert_eq!(5, mock.foo(4)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn clear() { | ||
| let mut mock = MockSimpleStruct::new(); | ||
| mock.expect_foo() | ||
| .returning(|x| i64::from(x) + 1); | ||
| assert_eq!(5, mock.foo(4)); | ||
|
|
||
| mock.expect_foo().returning(|x| i64::from(x) + 2); | ||
| assert_eq!(5, mock.foo(4)); | ||
|
|
||
| mock.clear_foo(); | ||
|
|
||
| mock.expect_foo().returning(|x| i64::from(x) + 2); | ||
| assert_eq!(6, mock.foo(4)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn clear_and_expect() { | ||
| let mut mock = MockSimpleStruct::new(); | ||
| mock.expect_foo() | ||
| .returning(|x| i64::from(x) + 1); | ||
| assert_eq!(5, mock.foo(4)); | ||
|
|
||
| mock.expect_foo().returning(|x| i64::from(x) + 2); | ||
| assert_eq!(5, mock.foo(4)); | ||
|
|
||
| mock.clear_and_expect_foo().returning(|x| i64::from(x) + 2); | ||
| assert_eq!(6, mock.foo(4)); | ||
| } | ||
|
|
||
| #[test] | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just like in automock_trait, you should use |
||
| fn calling_foo_without_expectation_after_clear() { | ||
| let mut mock = MockSimpleStruct::new(); | ||
| mock.expect_foo() | ||
| .returning(|x| i64::from(x) + 1); | ||
| assert_eq!(5, mock.foo(4)); | ||
|
|
||
| mock.clear_foo(); | ||
| let result = panic::catch_unwind(|| { | ||
| mock.foo(4) | ||
| }); | ||
| assert!(result.is_err()); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||
| //! automocking a trait | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #![deny(warnings)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::panic; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| use mockall::*; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| #[automock] | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -16,3 +17,48 @@ fn returning() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| .returning(|x| x + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(5, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn clear() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut mock = MockSimpleTrait::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.expect_foo() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .returning(|x| x + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(5, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.expect_foo().returning(|x| x + 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(5, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.clear_foo(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.expect_foo().returning(|x| x + 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(6, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn clear_and_expect() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut mock = MockSimpleTrait::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.expect_foo() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .returning(|x| x + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(5, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.expect_foo().returning(|x| x + 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(5, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.clear_and_expect_foo().returning(|x| x + 2); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(6, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn calling_foo_without_expectation_after_clear() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut mock = MockSimpleTrait::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.expect_foo() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .returning(|x| x + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(5, mock.foo(4)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.clear_foo(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let result = panic::catch_unwind(|| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mock.foo(4) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| assert!(result.is_err()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+64
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rust already provides us with a great way to assert that something panics. Let's use it.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -722,6 +722,119 @@ impl MockFunction { | |||||||
| ) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Generate code for the clear_ method | ||||||||
| /// | ||||||||
| /// # Arguments | ||||||||
| /// | ||||||||
| /// * `modname`: Name of the parent struct's private module | ||||||||
| // Supplying modname is an unfortunately hack. Ideally MockFunction | ||||||||
| // wouldn't need to know that. | ||||||||
| pub fn clear(&self, modname: &Ident) | ||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method contains much duplicated code from the |
||||||||
| -> impl ToTokens | ||||||||
|
Comment on lines
+732
to
+733
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| { | ||||||||
| let attrs = AttrFormatter::new(&self.attrs) | ||||||||
| .doc(false) | ||||||||
| .format(); | ||||||||
| let name = self.name(); | ||||||||
| let clear_ident = format_ident!("clear_{}", name); | ||||||||
| let funcname = &self.sig.ident; | ||||||||
| let (_, tg, _) = if self.is_method_generic() { | ||||||||
| &self.egenerics | ||||||||
| } else { | ||||||||
| &self.call_generics | ||||||||
| }.split_for_impl(); | ||||||||
| let (ig, _, wc) = self.call_generics.split_for_impl(); | ||||||||
| let mut wc = wc.cloned(); | ||||||||
| if self.is_method_generic() && (self.return_ref || self.return_refmut) { | ||||||||
| // Add Senc + Sync, required for downcast, since Expectation | ||||||||
| // stores an Option<#owned_output> | ||||||||
| send_syncify(&mut wc, self.owned_output.clone()); | ||||||||
| } | ||||||||
| let tbf = tg.as_turbofish(); | ||||||||
| let vis = &self.call_vis; | ||||||||
|
|
||||||||
| let substruct_obj = if let Some(trait_) = &self.trait_ { | ||||||||
| let ident = format_ident!("{trait_}_expectations"); | ||||||||
| quote!(#ident.) | ||||||||
| } else { | ||||||||
| quote!() | ||||||||
| }; | ||||||||
| let docstr = format!("Clear the [`Expectation`]({}/{}/struct.Expectation.html) array for the mocked `{}` method", | ||||||||
| modname, self.inner_mod_ident(), funcname); | ||||||||
| quote!( | ||||||||
| #[doc = #docstr] | ||||||||
| #(#attrs)* | ||||||||
| #vis fn #clear_ident #ig(&mut self) | ||||||||
| { | ||||||||
| self.#substruct_obj #name.clear #tbf(); | ||||||||
| } | ||||||||
| ) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Generate code for the clear_and_expect_ method | ||||||||
| /// | ||||||||
| /// # Arguments | ||||||||
| /// | ||||||||
| /// * `modname`: Name of the parent struct's private module | ||||||||
| /// * `self_args`: If supplied, these are the | ||||||||
| /// AngleBracketedGenericArguments of the self type of the | ||||||||
| /// trait impl. e.g. The `T` in `impl Foo for Bar<T>`. | ||||||||
| // Supplying modname is an unfortunately hack. Ideally MockFunction | ||||||||
| // wouldn't need to know that. | ||||||||
| pub fn clear_and_expect(&self, modname: &Ident, self_args: Option<&PathArguments>) | ||||||||
| -> impl ToTokens | ||||||||
| { | ||||||||
| let attrs = AttrFormatter::new(&self.attrs) | ||||||||
| .doc(false) | ||||||||
| .format(); | ||||||||
| let name = self.name(); | ||||||||
| let clear_and_expect_intent = format_ident!("clear_and_expect_{}", name); | ||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| let expectation_obj = self.expectation_obj(self_args); | ||||||||
| let funcname = &self.sig.ident; | ||||||||
| let (_, tg, _) = if self.is_method_generic() { | ||||||||
| &self.egenerics | ||||||||
| } else { | ||||||||
| &self.call_generics | ||||||||
| }.split_for_impl(); | ||||||||
| let (ig, _, wc) = self.call_generics.split_for_impl(); | ||||||||
| let mut wc = wc.cloned(); | ||||||||
| if self.is_method_generic() && (self.return_ref || self.return_refmut) { | ||||||||
| // Add Senc + Sync, required for downcast, since Expectation | ||||||||
| // stores an Option<#owned_output> | ||||||||
| send_syncify(&mut wc, self.owned_output.clone()); | ||||||||
| } | ||||||||
| let tbf = tg.as_turbofish(); | ||||||||
| let vis = &self.call_vis; | ||||||||
|
|
||||||||
| #[cfg(not(feature = "nightly_derive"))] | ||||||||
| let must_use = quote!(#[must_use = | ||||||||
| "Must set return value when not using the \"nightly\" feature" | ||||||||
| ]); | ||||||||
| #[cfg(feature = "nightly_derive")] | ||||||||
| let must_use = quote!(); | ||||||||
|
|
||||||||
| let substruct_obj = if let Some(trait_) = &self.trait_ { | ||||||||
| let ident = format_ident!("{trait_}_expectations"); | ||||||||
| quote!(#ident.) | ||||||||
| } else { | ||||||||
| quote!() | ||||||||
| }; | ||||||||
| let docstr = format!("Clear the [`Expectation`]({}/{}/struct.Expectation.html) array, before creating one for mocking the `{}` method", | ||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| modname, self.inner_mod_ident(), funcname); | ||||||||
| quote!( | ||||||||
| #must_use | ||||||||
| #[doc = #docstr] | ||||||||
| #(#attrs)* | ||||||||
| #vis fn #clear_and_expect_intent #ig(&mut self) | ||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
| -> &mut #modname::#expectation_obj | ||||||||
| #wc | ||||||||
| { | ||||||||
| self.#substruct_obj #name.clear #tbf(); | ||||||||
| self.#substruct_obj #name.expect #tbf() | ||||||||
| } | ||||||||
| ) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Return the name of this function's expecation object | ||||||||
| fn expectation_obj(&self, self_args: Option<&PathArguments>) | ||||||||
| -> impl ToTokens | ||||||||
|
|
@@ -1256,6 +1369,11 @@ impl ToTokens for CommonExpectationsMethods<'_> { | |||||||
| &mut self.0[__mockall_l - 1] | ||||||||
| } | ||||||||
|
|
||||||||
| #v fn clear(&mut self) | ||||||||
| { | ||||||||
| self.0.clear(); | ||||||||
| } | ||||||||
|
|
||||||||
| #v const fn new() -> Self { | ||||||||
| Self(Vec::new()) | ||||||||
| } | ||||||||
|
|
@@ -2576,6 +2694,11 @@ impl ToTokens for StaticGenericExpectations<'_> { | |||||||
| .unwrap() | ||||||||
| .expect() | ||||||||
| } | ||||||||
|
|
||||||||
| #v fn clear #ig (&mut self) | ||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like it will clear expectations for all combinations of generic arguments. Is that intended? I would think that it would be more useful to clear them for only one specific set of generic arguments. |
||||||||
| { | ||||||||
| self.store.clear(); | ||||||||
| } | ||||||||
| } | ||||||||
| ).to_tokens(tokens) | ||||||||
| } | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -155,6 +155,21 @@ impl MockTrait { | |||||||||
| meth.expect(modname, Some(path_args)) | ||||||||||
| } | ||||||||||
| }).collect::<Vec<_>>(); | ||||||||||
| let clears = self.methods.iter() | ||||||||||
| .filter(|meth| !meth.is_static()) | ||||||||||
| .map(|meth| | ||||||||||
| meth.clear(modname) | ||||||||||
| ).collect::<Vec<_>>(); | ||||||||||
|
Comment on lines
+160
to
+162
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| let clear_and_expects = self.methods.iter() | ||||||||||
| .filter(|meth| !meth.is_static()) | ||||||||||
| .map(|meth| { | ||||||||||
| if meth.is_method_generic() { | ||||||||||
| // Specific impls with generic methods are TODO. | ||||||||||
| meth.clear_and_expect(modname, None) | ||||||||||
| } else { | ||||||||||
| meth.clear_and_expect(modname, Some(path_args)) | ||||||||||
| } | ||||||||||
| }).collect::<Vec<_>>(); | ||||||||||
| let trait_path = &self.trait_path; | ||||||||||
| let self_path = &self.self_path; | ||||||||||
| let types = &self.types; | ||||||||||
|
|
@@ -169,6 +184,8 @@ impl MockTrait { | |||||||||
| #(#impl_attrs)* | ||||||||||
| impl #ig #self_path #wc { | ||||||||||
| #(#expects)* | ||||||||||
| #(#clears)* | ||||||||||
| #(#clear_and_expects)* | ||||||||||
| #(#contexts)* | ||||||||||
| } | ||||||||||
| ) | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.