Edit: This post is actually about a weird NMock error message. As Nigel Thorne so kindly pointed out, this way of testing an IEnumerable is kind of awkward and cumbersome, as I should have just done something like this:
1 2 3 |
Stub.On(someObjectContainingOccurrences) .Method("GetOccurrences") .Will(Return.Value(new DateTime?[]{ /* some values in here */ })); |
Please use this approach, unless your test subject is actually the implementation of the
foreach construct ๐
Original post:
A recent problem I had was when I attempted to mock a function returning an
IEnumerable<T>. I went about and punched in somthing that I expected to work – something like this:
1 2 3 4 5 6 7 8 9 10 11 |
var enumerator = mocks.NewMock<IEnumerator<DateTime?>>(); Stub.On(enumerator).Method("Dispose"); var enumerable = mocks.NewMock<IEnumerable<DateTime?>>(); Stub.On(enumerable) .Method("GetEnumerator") .Will(Return.Value(enumerator)); Expect.Once.On(someObjectContainingOccurrences) .Method("GetOccurrences") .Will(Return.Value(enumerable)); |
– and then I set some expectations on the usage:
1 2 3 4 |
Expect.Once.On(enumerator).Method("MoveNext"); Expect.Once.On(enumerator).GetProperty("Current").Will(Return.Value((DateTime?)new DateTime(2003, 11, 12))); Expect.Once.On(enumerator).Method("MoveNext"); // [...] stuff like that... |
– and the usage inside my class under test was something like this:
1 2 3 4 |
foreach(var occurrence in someObjectContainingOccurrences.GetOccurrences()) { // do stuff to the occurrence in here } |
– but then I ran the test, and I got this cryptic error message:
1 2 3 4 5 6 7 8 |
System.Runtime.Remoting.RemotingException: ByRef value type parameter cannot be null. at System.Runtime.Remoting.Proxies.RealProxy.ValidateReturnArg(Object arg, Type paramType) at System.Runtime.Remoting.Proxies.RealProxy.PropagateOutParameters(IMessage msg, Object[] outArgs, Object returnValue) at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref MessageData msgData, Int32 type) at System.Collections.IEnumerator.MoveNext() at System.Collections.IEnumerator.MoveNext() [...] |
This was really annoying! Especially due to the fact that the foreach prevented me from seeing what was actually going on. It took me a few minutes to realize that the error came from me being too sloppy to check out the IEnumerator interface I was mocking – the interface looks like this:
1 2 3 |
void Reset(); bool MoveNext(); object Current { get; } |
(where the generic IEnumerator<T> interface inherits from IEnumerator, narrowing the return type of the Current property down to objects of type T).
Can you spot the error?
The return type of MoveNext is bool!!!
When I am mocking a method call like this:
1 |
Expect.Once.On(enumerator).Method("MoveNext"); |
NMock will check the method signature – and if the signature has a return type other than void, NMock tries to be nice and – instead of emitting an error (which IMO would be appropriate) – returns null! And since null is a reference type, but the expected type was bool, I got the cryptic RemotingException.
The solution was obvious:
1 |
Expect.Once.On(enumerator).Method("MoveNext").Will(Return.Value(true)); |
PS: I am posting this to avoid having this problem again (for too long). I remember having had this error before, but it took me almost 30 minutes to realize what was wrong. Hopefully, next time I will remember ๐