RajOpteamix / moq

Automatically exported from code.google.com/p/moq
Other
0 stars 0 forks source link

Unable to verify that a method has been called with a specific property only once #195

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
I've attached the source code which demonstrates the issue.

Basically I am writing a test as follows:
int expectedMyProperty = 5;
myClass.DoIt(expectedMyProperty);

//Here I am wanting to verify that Save was only called once for the time
where MyProperty was set to the value I am expecting myClass2Mock.Verify(p
=> p.Save(It.Is<MyClass3>(m => m.MyProperty == expectedMyProperty)),
Times.Once());

The DoIt method does this:
MyClass3 mc3 = new MyClass3();
myClass2.Save(mc3); //save without setting the property
mc3.MyProperty = myProperty;
myClass2.Save(mc3); //save after setting the property

What is the expected output? What do you see instead?
I expect the test to pass in this instance as the method has only ever been
called once with the circumstances I specified.

Instead I get an error message stating:
Moq.MockException: 
Invocation was performed more than once on the mock: p =>
p.Save(It.Is<MyClass3>(m => (m.MyProperty =
value(MoqBugDemonstrations.TestMyClass+<>c__DisplayClass0).expectedMyProperty)))

What version of the product are you using? On what operating system?
Moq 3.1.416.3 in Visual Studio 2008 on Windows XP.

Please provide any additional information below.

Original issue reported on code.google.com by joshuajr...@gmail.com on 30 Jul 2009 at 4:45

Attachments:

GoogleCodeExporter commented 8 years ago
You're getting that behaviour because the .Verify is executed and the method 
calls
are matched against the machine state that exists *after* all the calls have 
taken
place (and been recorded).

If you imagine what Moq recorded during execution, it *recorded* one call where 
you
called Save() with your MyClass3 instance, and then made another call to Save() 
with
your MyClass3 instance.  (It's not really feasible for Moq to do a deep clone 
of your
parameters, so it didn't record that your MyClass3 instance had one property 
value at
the first call and a different value at the second call.)

Then when you come along and talk to it about 'calls to Save where the MyClass3 
had
the desired property', then at that point, both calls match because right then, 
the
(one) MyClass3 instance matches your constraint.

You might have better luck if you specified your constraint as a pre-call
.Setup.Verifiable() instead of using a post-call .Verify().  That's because the 
match
is then run against the parameters as they happen rather than as they are when 
you
call .Verify().

But unfortunately there's no overload of .Setup/.Verifiable() that takes a Times
object (as you use in .Verify).   Without that, you end up with a setup that 
checks
that it's called at least once, not exactly once:

// Not quite right - checks at least one matching call
myClass2Mock
    .Setup(p => p.Save(It.Is<MyClass3>(m => m.MyProperty == expectedMyProperty)))
    .Verifiable();
DoIt(myClass2Mock.Object);
myClass2Mock.Verify();

But fortunately there is a method '.AtMostOnce()' that you can call on a .Setup
result, which, in combination with .Verify() gives you 'precisely once'.  But
unfortunately that's deprecated in favour of the Times objects on .Verify 
calls...
which don't work in your case for the reasons stated above! :(

// Right - checks for precisely one matching call with state
// - but uses a deprecated call
myClass2Mock
    .Setup(p => p.Save(It.Is<MyClass3>(m => m.MyProperty == expectedMyProperty)))
    .AtMostOnce()
    .Verifiable();
DoIt(myClass2Mock.Object);
myClass2Mock.Verify();

I've come across this particular issue before myself, where I've wanted to use a
strict mock to check that something happened, but wanted a constraint on the 
count.  

Perhaps arranging for the .Verifiable() method on ISetup<T> to take a Times 
object
would solve both problems...

Original comment by RoystonS...@googlemail.com on 6 Mar 2010 at 4:59

GoogleCodeExporter commented 8 years ago
I agree that allowing Times to be passed in to a setup (maybe as an argument to 
Verifiable?) would be good. 

That would not solve the underlying inconsistency here between Verifiable and 
Verify, 
unfortunately :(

Original comment by kzu.net on 8 Mar 2010 at 4:35

GoogleCodeExporter commented 8 years ago
I'm thinking about un-deprecating the occurrence constraints on setups. 

Thoughts?

Original comment by kzu.net on 11 Aug 2010 at 4:59