-->

Mocking Delegate.Invoke() using Moq throws Invalid

2020-07-02 09:46发布

问题:

Let's say that I have IService interface:

public interface IService
{
    string Name { get; set; }
}

And a delegate Func<IService> that returns this interface.

In my unit test I want to mock the delegate's Invoke() method using Moq like this:

[TestMethod]
public void UnitTest()
{
    var mockService = new Mock<IService>();

    var mockDelegate = new Mock<Func<IService>>();
    mockDelegate.Setup(x => x.Invoke()).Returns(mockService.Object);

    // The rest of the test
}

Unfortunately mockDelegate.Setup(...) throws System.InvalidCastException:

Test method UnitTest threw exception:

System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.InvocationExpression'.

at Moq.ExpressionExtensions.GetCallInfo(LambdaExpression expression, Mock mock)

at Moq.Mock.<>c_DisplayClass1c`2.b_1b()

at Moq.PexProtector.Invoke(Func`1 function)

at Moq.Mock.Setup(Mock1 mock, Expression1 expression, Condition condition)

at Moq.Mock1.Setup(Expression1 expression)

at UnitTest() in UnitTests.cs: line 38

Line 38 is mockDelegate.Setup(x => x.Invoke()).Returns(mockService.Object);

Am I missing something? Or mocking delegate invocation is generally not a good idea?

Thank you.

回答1:

It is 100% possible to do this in Moq, here is how:

var mockService = new Mock<IService>();

var mockDelegate = new Mock<Func<IService>>();
mockDelegate.Setup(x => x()).Returns(mockService.Object);

The reason you were getting the InvalidCastException was because you are creating a Mock<T> of a delegate type. Thus it is expecting the Expression to be of type InvocationExpression (x()) rather than InstanceMethodCallExpressionN (x.Invoke()).

This also allows you to verify invocations of your Mock delegate, e.g.

mockDelegate.Verify(x => x(), Times.Once);

I have posted this as an answer because while it may not be necessary for this situation, it can certainly be useful to know.



回答2:

This answer is a summary of SLaks and nemesv comments.

There is no reason to mock Func<IService> delegate in the first place. Instead one can write:

[TestMethod]
public void UnitTest()
{
    var mockService = new Mock<IService>();

    Func<IService> mockDelegate = () => mockService.Object;

    // The rest of the test
}

The exception is there because because this scenario is not supported by Moq. But instead of throwing a NotSupportException you get not so nice InvalidCastException.