Skip to content

Commit

Permalink
Merge pull request #246 from asomers/better_nomatch_msg
Browse files Browse the repository at this point in the history
Print better messages for "No matching expectation found"
  • Loading branch information
asomers authored Jan 24, 2021
2 parents f4ee04f + de5a43e commit 7602b06
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 17 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added

- When a test fails because a mock object receives an unexpected call, the
error message will now show the method's arguments. This requires the
`nightly` feature, and requires that the arguments implement `Debug`.
([#246](https://github.com/asomers/mockall/pull/246))

### Changed
### Fixed
### Removed

## [0.9.0] - 2020-12-21
### Added

Expand Down
26 changes: 26 additions & 0 deletions mockall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,7 @@
use downcast::*;
use std::{
any,
fmt::{self, Debug, Formatter},
marker::PhantomData,
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
RangeToInclusive},
Expand Down Expand Up @@ -1358,6 +1359,31 @@ pub struct DefaultReturner<O>(PhantomData<O>);
}
}

#[doc(hidden)]
pub struct MaybeDebugger<'a, T>(pub &'a T);
::cfg_if::cfg_if! {
if #[cfg(feature = "nightly")] {
impl<'a, T> Debug for MaybeDebugger<'a, T> {
default fn fmt(&self, f: &mut Formatter<'_>)
-> Result<(), fmt::Error>
{
write!(f, "?")
}
}
impl<'a, T: Debug> Debug for MaybeDebugger<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
self.0.fmt(f)
}
}
} else {
impl<'a, T> Debug for MaybeDebugger<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "?")
}
}
}
}

// Though it's not entirely correct, we treat usize::max_value() as
// approximately infinity.
#[derive(Debug)]
Expand Down
7 changes: 6 additions & 1 deletion mockall/tests/automock_foreign_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ fn normal_usage() {
}

#[test]
#[should_panic(expected = "mock_ffi::foo1: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "mock_ffi::foo1(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "mock_ffi::foo1(?): No matching expectation found"
))]
fn with_no_matches() {
let ctx = mock_ffi::foo1_context();
ctx.expect()
Expand Down
7 changes: 6 additions & 1 deletion mockall/tests/automock_foreign_extern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ extern "C" {
}

#[test]
#[should_panic(expected = "mock_ffi::foo1: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "mock_ffi::foo1(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "mock_ffi::foo1(?): No matching expectation found"
))]
fn with_no_matches() {
let ctx = mock_ffi::foo1_context();
ctx.expect()
Expand Down
7 changes: 6 additions & 1 deletion mockall/tests/automock_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ mod m {
}

#[test]
#[should_panic(expected = "mock_foo::bar1: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "mock_foo::bar1(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "mock_foo::bar1(?): No matching expectation found"
))]
fn with_no_matches() {
let ctx = mock_foo::bar1_context();
ctx.expect()
Expand Down
27 changes: 27 additions & 0 deletions mockall/tests/automock_nondebug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// vim: tw=80
//! A method may have non-Debug arguments and/or return values.
#![deny(warnings)]

use mockall::*;

// Don't derive Debug
pub struct NonDebug(u32);

#[automock]
pub trait Foo {
fn foo(&self, x: NonDebug);
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
fn with_no_matches() {
let mock = MockFoo::new();
mock.foo(NonDebug(5));
}


7 changes: 6 additions & 1 deletion mockall/tests/automock_slice_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ mod withf {
use super::*;

#[test]
#[should_panic(expected = "MockFoo::foo: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo([1, 2, 3, 4]): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
fn fail() {
let mut mock = MockFoo::new();
mock.expect_foo()
Expand Down
7 changes: 6 additions & 1 deletion mockall/tests/mock_generic_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ mod with {
}

#[test]
#[should_panic(expected = "MockFoo::foo: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(4): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
fn wrong_generic_type() {
let mut mock = MockFoo::new();
mock.expect_foo::<i16>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ fn ctx_checkpoint() {

// Expectations should be cleared when a context object drops
#[test]
#[should_panic(expected = "MockFoo::foo3: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo3(42, 69): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo3(?, ?): No matching expectation found"
))]
fn ctx_hygiene() {
{
let ctx0 = MockFoo::<u32>::foo3_context();
Expand Down
21 changes: 18 additions & 3 deletions mockall/tests/mock_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ mod checkpoint {
}

#[test]
#[should_panic(expected = "MockFoo::foo: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(0): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
fn removes_old_expectations() {
let mut mock = MockFoo::new();
mock.expect_foo()
Expand Down Expand Up @@ -118,7 +123,12 @@ mod r#match {
}

#[test]
#[should_panic(expected = "MockFoo::bar: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::bar(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::bar(?): No matching expectation found"
))]
fn with_no_matches() {
let mut mock = MockFoo::new();
mock.expect_bar()
Expand Down Expand Up @@ -146,7 +156,12 @@ mod r#match {
}

#[test]
#[should_panic(expected = "MockFoo::bar: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::bar(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::bar(?): No matching expectation found"
))]
fn withf_no_matches() {
let mut mock = MockFoo::new();
mock.expect_bar()
Expand Down
7 changes: 6 additions & 1 deletion mockall/tests/mock_struct_with_static_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ fn ctx_checkpoint() {

// Expectations should be cleared when a context object drops
#[test]
#[should_panic(expected = "MockFoo::bar3: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::bar3(42): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::bar3(?): No matching expectation found"
))]
fn ctx_hygiene() {
{
let ctx0 = MockFoo::bar3_context();
Expand Down
7 changes: 6 additions & 1 deletion mockall/tests/mock_unsized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ fn with_eq() {
}

#[test]
#[should_panic(expected = "MockFoo::foo: No matching expectation found")]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(\"xxx\"): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
fn with_never() {
let mut foo = MockFoo::new();
foo.expect_foo()
Expand Down
19 changes: 13 additions & 6 deletions mockall_derive/src/mock_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,12 +432,17 @@ impl MockFunction {
}.split_for_impl();
let tbf = tg.as_turbofish();
let name = self.name();
let no_match_msg = if let Some(s) = &self.struct_ {
format!("{}::{}: No matching expectation found", s, name)
let argnames = &self.argnames;
let fields = vec!["{:?}"; argnames.len()].join(", ");
let fstr = if let Some(s) = &self.struct_ {
format!("{}::{}({}): No matching expectation found",
s, name, fields)
} else {
format!("{}::{}: No matching expectation found", self.mod_ident,
self.name())
format!("{}::{}({}): No matching expectation found",
self.mod_ident, self.name(), fields)
};
let no_match_msg = quote!(format!(#fstr,
#(::mockall::MaybeDebugger(&#argnames)),*));
let sig = &self.sig;
let vis = if self.trait_.is_some() {
&Visibility::Inherited
Expand All @@ -461,6 +466,7 @@ impl MockFunction {
// Don't add a doc string. The original is included in #attrs
#(#attrs)*
#vis #sig {
let no_match_msg = #no_match_msg;
{
let __mockall_guard = #outer_mod_path::EXPECTATIONS
.lock().unwrap();
Expand All @@ -472,16 +478,17 @@ impl MockFunction {
/* std::panic::catch_unwind(|| */
__mockall_guard.#call#tbf(#(#call_exprs,)*)
/*)*/
}.expect(#no_match_msg)
}.expect(&no_match_msg)
}
)
} else {
quote!(
// Don't add a doc string. The original is included in #attrs
#(#attrs)*
#vis #sig {
let no_match_msg = #no_match_msg;
self.#substruct_obj #name.#call#tbf(#(#call_exprs,)*)
.expect(#no_match_msg)
.expect(&no_match_msg)
}

)
Expand Down

0 comments on commit 7602b06

Please sign in to comment.