202
votes

I have a preexisting Interface...

public interface ISomeInterface
{
    void SomeMethod();
}

and I've extended this intreface using a mixin...

public static class SomeInterfaceExtensions
{
    public static void AnotherMethod(this ISomeInterface someInterface)
    {
        // Implementation here
    }
}

I have a class thats calling this which I want to test...

public class Caller
{
    private readonly ISomeInterface someInterface;

    public Caller(ISomeInterface someInterface)
    {
        this.someInterface = someInterface;
    }

    public void Main()
    {
        someInterface.AnotherMethod();
    }
}

and a test where I'd like to mock the interface and verify the call to the extension method...

    [Test]
    public void Main_BasicCall_CallsAnotherMethod()
    {
        // Arrange
        var someInterfaceMock = new Mock<ISomeInterface>();
        someInterfaceMock.Setup(x => x.AnotherMethod()).Verifiable();

        var caller = new Caller(someInterfaceMock.Object);

        // Act
        caller.Main();

        // Assert
        someInterfaceMock.Verify();
    }

Running this test however generates an exception...

System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()

My question is, is there a nice way to mock out the mixin call?

6
In my experience, the terms mixin and extension methods are separate things. I'd use the latter in this instance to avoid mixups :PRuben Bartelink

6 Answers

37
votes

I have used a Wrapper to get around this problem. Create a wrapper object and pass your mocked method.

See Mocking Static Methods for Unit Testing by Paul Irwin, it has nice examples.

37
votes

You can't "directly" mock static method (hence extension method) with mocking framework. You can try Moles (http://research.microsoft.com/en-us/projects/pex/downloads.aspx), a free tool from Microsoft that implements a different approach. Here is the description of the tool:

Moles is a lightweight framework for test stubs and detours in .NET that is based on delegates.

Moles may be used to detour any .NET method, including non-virtual/static methods in sealed types.

You can use Moles with any testing framework (it's independent about that).

20
votes

I found that I had to discover the inside of the extension method I was trying to mock the input for, and mock what was going on inside the extension.

I viewed using an extension as adding code directly to your method. This meant I needed to mock what happens inside the extension rather than the extension itself.

3
votes

I like to use the wrapper (adapter pattern) when I am wrapping the object itself. I'm not sure I'd use that for wrapping an extension method, which is not part of the object.

I use an internal Lazy Injectable Property of either type Action, Func, Predicate, or delegate and allow for injecting (swapping out) the method during a unit test.

    internal Func<IMyObject, string, object> DoWorkMethod
    {
        [ExcludeFromCodeCoverage]
        get { return _DoWorkMethod ?? (_DoWorkMethod = (obj, val) => { return obj.DoWork(val); }); }
        set { _DoWorkMethod = value; }
    } private Func<IMyObject, string, object> _DoWorkMethod;

Then you call the Func instead of the actual method.

    public object SomeFunction()
    {
        var val = "doesn't matter for this example";
        return DoWorkMethod.Invoke(MyObjectProperty, val);
    }

For a more complete example, check out http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/

2
votes

If you just want to make sure that the extension method was invoked, and you aren't trying to setup a return value, then you can check the Invocations property on the mocked object.

Like this:

var invocationsCount = mockedObject.Invocations.Count;
invocationsCount.Should().BeGreaterThan(0);
-2
votes

So if you are using Moq, and want to mock the result of an Extension method, then you can use SetupReturnsDefault<ReturnTypeOfExtensionMethod>(new ConcreteInstanceToReturn()) on the instance of the mock class that has the extension method you are trying to mock.

It is not perfect, but for unit testing purposes it works well.