Search code examples
c#.net-corexunitnsubstitute

Can I set-up a mock to return regardless of generic type?


Code I'm attempting to test with unit tests does the following:

void DoSomething()
{
   var myTypeImplementation = _lifetimeScope.Resolve<IMyInterface<MyType>>();
   var mySecondTypeImplementation = _lifetimeScope.Resolve<IMyInterface<MySecondType>>();

   myTypeImplementation.Bar();
   mySecondTypeImplementation.Bar();
}

Question

How can I mock _lifetimeScope (ILifetimeScope) by handling the generic type implicitly?

What I've Tried

I have the following and it works, but there are many more types than just the 2 I've used in my example, so there's a lot of copy/paste:

        var mockFirstType = Substitute.For<IMyInterface<MyType>>();
        _mockLifetimeScope.Resolve<IMyInterface<MyType>().Returns(mockFirstType);

        
        var mockSecondType = Substitute.For<IMyInterface<MySecondType>>();
        _mockLifetimeScope.Resolve<IMyInterface<MySecondType>().Returns(mockSecondType);

After this set-up, I still need to assert, so now I have an assertion per type:

        mockFirstType.Received().Bar();
        mockSecondType.Received().Bar();

Instead, the ideal would be to write this test as a Theory and pass a Type maybe? But some alternative approach where I don't handle types explicitly would be a lot neater.


Solution

  • Option 1:

    [Fact]
    public void TestMethod()
    {
        var myFirstVerificationCallback = GetVerificationCallback<MyType>();
        var mySecondVerificationCallback = GetVerificationCallback<MySecondType>();
        var myThirdVerificationCallback = GetVerificationCallback<MyThirdType>();
    
        myFirstVerificationCallback();
        mySecondVerificationCallback();
        myThirdVerificationCallback();
    }
    
    private Action GetVerificationCallback<T>()
    {
        var typeMock = Substitute.For<IMyInterface<T>>();
        
        _mockLifetimeScope
            .Resolve<IMyInterface<T>>()
            .Returns(typeMock);
    
        return () => typeMock
            .Received()
            .Bar();
    }
    

    Option 2 (maybe):

    [Theory]
    [InlineData(typeof(MyType))]
    [InlineData(typeof(MySecondType))]
    [InlineData(typeof(MyThirdType))]
    public void TestMethod(Type typeParameterType)
    {
        var serviceTypeToMock = typeof(IMyInterface<>).MakeGenericType(typeParameterType);
        var substitution = Substitute.For(new Type[] {serviceTypeToMock}, Array.Empty<object>());
    
        _mockLifetimeScope
            .Resolve(serviceTypeToMock)
            .Returns(substitution);
    
        (substitution.Received() as dynamic).Bar();
    }