Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Delegate.Invoke() using Moq throws InvalidCast exception in LINQ

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.

like image 324
Nikolai Samteladze Avatar asked Jan 23 '14 01:01

Nikolai Samteladze


2 Answers

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.

like image 161
Lukazoid Avatar answered Oct 10 '22 18:10

Lukazoid


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.

like image 28
Nikolai Samteladze Avatar answered Oct 10 '22 18:10

Nikolai Samteladze