I’ve seen a few people get bitten by an edge case of using generics with mocking frameworks. Let’s look at a contrived example and see what’s going on. We’re using Rhino Mocks 3.5 in this case, but the symptoms described here are not specific to that mocking framework, but rather are related to how generics work in .NET.
public interface IFish{} public class Eric : IFish{} public interface IAquarium { IEnumerable<T> FindEverythingLike<T>(T thisThing); } [Test] public void LookingForALicenceForMyPetFishEric() { var petFish = new Eric(); var expectedErics = new[] {petFish}; IAquarium stubAquarium = MockRepository.GenerateStub<IAquarium>(); stubAquarium .Stub(aquarium => aquarium.FindEverythingLike(Arg<IFish>.Is.Anything)) .Return(expectedErics); var actualErics = stubAquarium.FindEverythingLike(petFish); Assert.That(actualErics, Is.EqualTo(expectedErics)); }
Here we’re asking our stubbed IAquarium
to return an expectedErics
array whenever it gets any IFish
as an argument to the generic FindEverythingLike<T>()
method. We then pass our petFish
to the method and get the actual enumerable returned from our stub. So what does this assertion give us?
MockingGenerics.LookingForALicenceForMyPetFishEric : FailedNUnit.Framework.AssertionException: Expected: < <Workshop.Tests.Eric> > But was: null
Oops, our stubbed value hasn’t been returned. Our petFish
is an instance of class Eric
, which implements IFish
, so why isn’t our stub returning our expectedErics
?
The reason for this is that we are actually stubbing out FindEverythingLike<IFish>()
, but are calling FindEverythingLike<Eric>()
. While we declare this as a single generic method, the CLR actually calls these as two completely different method instances. This is obscured a bit by the fact that type inference is used to determine which method instance is called. If we don’t rely on type inference we can get the test to pass:
var actualErics = stubAquarium.FindEverythingLike<IFish>(petFish); Assert.That(actualErics, Is.EqualTo(expectedErics));
In the real examples where I’ve seen errors like this we generally have more collaborators involved and the problem becomes harder to detect. It’s not a common case, requiring us passing different static types to a generic method where at first glance it looks reasonable to expect our call to resolve to the one method.
Just to belabour the point for one more example, let’s just confirm that it is the static type of our reference that determines which method instance is called, rather than the actual instance type:
[Test] public void GenericsAndMockingCanBeTroublesome() { IAquarium mockAquarium = MockRepository.GenerateMock<IAquarium>(); Eric petFish = new Eric(); IFish sameFish = petFish; mockAquarium.FindEverythingLike(petFish); mockAquarium.AssertWasCalled(aquarium => aquarium.FindEverythingLike(sameFish)); }
This case fails, as we are calling mockAquarium.FindEverythingLike<Eric>()
, but asserting that FindEverythingLike<IFish>()
was called. Even though it is the same instance, the declared types of each reference at compile time are the ones that are used to dispatch to our generic method instance.
Hope this helps save someone a couple of minutes of frustration. :)