简体   繁体   中英

Extension Method on Moq returns null

I try to test the result of some function where a call to an extension method is used. This extension method is defined on an interface. The test setup creates a mock of said interface. For this mock two setups are configured. When calling these setup function on the mocked interface implementation, everything works as intended. (see TestMockSetupSourceClassA and TestMockSetupSourceClassB) But when these calls are made in the extension method the result is null. (see TestDoClassStuff)

I've set up a test project: https://github.com/sschauss/MoqExtensionMethodTest

Extension

public static class ExtensionClass
{
    public static TResult DoExtensionStuff<TResult>(this ISomeInterface someInterface, object initialObject,
        params object[] objects)
    {
        var result = someInterface.DoInterfaceStuff<TResult>(initialObject);
        return objects.Aggregate(result, (agg, cur) => someInterface.DoInterfaceStuff(cur, agg));
    }
}

Implementation

public class SomeClass
{
    private readonly ISomeInterface _someInterface;

    public SomeClass(ISomeInterface someInterface)
    {
        _someInterface = someInterface;
    }

    public TargetClass DoClassStuff(SourceClassA sourceClassA, SourceClassB sourceClassB)
    {
        return _someInterface.DoExtensionStuff<TargetClass>(sourceClassA, sourceClassB);
    }
}

Test

public class UnitTest
{
    private readonly SomeClass _sut;
    private readonly SourceClassA _sourceA;
    private readonly SourceClassB _sourceB;
    private readonly TargetClass _target;
    private readonly Mock<ISomeInterface> _someInterfaceMock;

    public UnitTest()
    {
        _sourceA = new SourceClassA
        {
            Integer = 1
        };
        _sourceB = new SourceClassB
        {
            String = "stringB"
        };
        _target = new TargetClass
        {
            Integer = 2,
            String = "stringT"
        };
        _someInterfaceMock = new Mock<ISomeInterface>();
        _someInterfaceMock.Setup(m => m.DoInterfaceStuff<TargetClass>(_sourceA)).Returns(_target);
        _someInterfaceMock.Setup(m => m.DoInterfaceStuff(_sourceB, _target)).Returns(_target);
        _sut = new SomeClass(_someInterfaceMock.Object);
    }

    [Fact]
    public void TestDoClassStuff()
    {
        var result = _sut.DoClassStuff(_sourceA, _sourceB);

        result.Should().BeEquivalentTo(_target);
    }

    [Fact]
    public void TestMockSetupSourceClassA()
    {
        var result = _someInterfaceMock.Object.DoInterfaceStuff<TargetClass>(_sourceA);

        result.Should().BeEquivalentTo(_target);
    }

    [Fact]
    public void TestMockSetupSourceClassB()
    {
        var result = _someInterfaceMock.Object.DoInterfaceStuff(_sourceB, _target);

        result.Should().BeEquivalentTo(_target);
    }
}

The problem has to do with the Aggregate extension, its generic argument parameters and what you have Setup the mock to expect.

The params of the extension method DoExtensionStuff is an object array so when calling the `

T2 DoInterfaceStuff<T1, T2>(T1 parameter1, T2 parameter2)

within the Aggregate delegate you are actually passing

 (TResult agg, object cur) => someInterface.DoInterfaceStuff<object,TResult>(cur, agg)

which the mock was not configured to handle.

After changing the _someInterfaceMock.Setup , in this particular case, explicitly to

 _someInterfaceMock
    .Setup(m => m.DoInterfaceStuff<object, TargetClass>(_sourceB, _target))
    .Returns(_target);

All the tests in this scenario were able to be exercised to completion successfully.

The thing with Moq is that when a mock is not told explicitly what to expect it will return null by default for reference types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM