Disclamer: although I'm pretty convinced there's a better way to do this, I haven't found it, so here goes...
Suppose I have this interface:
public interface IFoo
{
int Add(int a, int b);
}
Now I’d like to mock it (using NMock2), like so:
IFoo mock = new Mockery().NewMock<IFoo>();
Expect.Once.On(mock).Method("Add").With(5, Is.Anything).Will(Return.Value(10));
Console.WriteLine(mock.Add(5, 6));
Leaving aside why I’d want it to return 10 regardless of the value I add to 5 J, the above doesn’t work. More specifically, calling mock.Add(5, 6) throws.
The reason is that the call to With will resolve to the actual “With(Object[])” method, which will internally build an array of EqualMatcher objects, set their expected value to the expected parameter values (5 and Is.Anything) and call “Matches(object actualParameter)” on them, which finally calls Equals(expectedParameterValue, actualParameterValue) . Looking at the call above, 5 will obviously be equal to 5, but Is.Anything is actually an instance of the AlwaysMatcher class, and Equals(6, alwaysMatcherInstance) is obviously false.
The trick to fix this is to use the other With() overload: With(Matcher[]), like so:
Expect.Once.On(mock).Method("Add").With(new EqualMatcher(5), new AlwaysMatcher(true, "bla")).Will(Return.Value(10));
Looks crappy and is crappy, but it works, because this will mean that each of the two matcher’s specified above Matches() methods will be called (and AlwaysMatcher just returns the value given in its constructor, in our case “true”). Basically before we had two EqualMatchers, now we have one EqualMatcher and an AlwaysMatcher.
With this in mind, the next line is almost the same and also works:
Expect.Once.On(mock).Method("Add").With(new EqualMatcher(5), Is.Anything).Will(Return.Value(10));
Update, the following
also works (thanks to
Nigel Thorne):
Expect.Once.On(mock).Method("Add").With(Is.EqualTo(5), Is.Anything).Will(Return.Value(10));
Hope this helps someone
:)
Update 12 may 2008: a similar scenario can be encountered when mocking a call to a method which has a params parameter like, for example, DoFoo(params object[] someParams). The problem here is that when defining the parameter values for this method, the NMock method With(object[] params) will (as explained above) loop through the object array and create a matcher for each one, but the actual call to the mocked (DoFoo) method will be with only one parameter (the object[] someParams). So, at runtime, NMock will see that you called a DoFoo(object[]) with one parameter, but defined an expectation of N parameters (the N matchers he created) and will fail.
To fix this, when defining the expectation, you need to enclose the parameter values in one single matcher, so instead of
Expect.Once.On(someMock).Method("DoFoo").With(paramValue1, paramValue2, ..., paramValueN).Will...
do
Expect.Once.On(someMock).Method("DoFoo").With(Is.EqualTo(new object[] {paramValue1, paramValue2, ..., paramValueN})).Will...