Occasional trouble mocking generic methods

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. :)

Comments