使用Moq模拟Delegate.Invoke()在LINQ中引发InvalidCast异常

18

假设我有一个 IService 接口:

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

还有一个委托 Func<IService>,返回这个接口。

在我的单元测试中,我想使用Moq来模拟委托的 Invoke() 方法,就像这样:

[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
}

不幸的是,mockDelegate.Setup(...) 抛出了 System.InvalidCastException 异常:

测试方法 UnitTest 抛出异常:

System.InvalidCastException: 无法将类型为“System.Linq.Expressions.InstanceMethodCallExpressionN”的对象强制转换为类型“System.Linq.Expressions.InvocationExpression”。

在 Moq.ExpressionExtensions.GetCallInfo(LambdaExpression expression, Mock mock) 中

在 Moq.Mock.<>c_DisplayClass1c`2.b_1b() 中

在 Moq.PexProtector.Invoke(Func`1 function) 中

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

在 Moq.Mock1.Setup(Expression1 expression) 中

在 UnitTest() 中,位于 UnitTests.cs 的第 38 行

第 38 行是:mockDelegate.Setup(x => x.Invoke()).Returns(mockService.Object);

我是否漏掉了什么?或者说,通常模拟委托调用并不是一个好主意吗?

谢谢。


3
没必要那么做,你可以自己委派一个人。 - SLaks
谢谢!不过还是很有趣为什么会出现异常... - Nikolai Samteladze
1
@NikolaiSamteladze 这个异常是因为 Moq 不支持这种情况,但是它不会抛出 NotSupportException 而是抛出了一个不太友好的 InvalidCastException。在我看来这是一个 bug,所以应该修复异常类型... 但是你无论如何都不应该模拟委托,而是应该写成:Func<IService> mockDelegate = () => mockService.Object; - nemesv
是的,我确实做到了。感谢您提供有关异常的详细信息。 - Nikolai Samteladze
已添加摘要回答作为答案。如果明天没有人关心提交其他答案,我将接受它。 - Nikolai Samteladze
如果您看到我的答案,您可以看到Moq支持此场景,异常是因为委托的配置不正确。 - Lukazoid
2个回答

43

在Moq中完全可以做到这一点,以下是方法:

var mockService = new Mock<IService>();

var mockDelegate = new Mock<Func<IService>>();
mockDelegate.Setup(x => x()).Returns(mockService.Object);
由于您正在创建委托类型的Mock<T>,因此导致出现InvalidCastException错误。因此,它期望ExpressionInvocationExpression类型(x()),而不是InstanceMethodCallExpressionN类型(x.Invoke())。
这也允许您验证Mock委托的调用,例如:
mockDelegate.Verify(x => x(), Times.Once);

我将这篇文章发布为答案,因为尽管这可能不是必要的情况,但了解这一点肯定是有用的。


谢谢!我一定会今天尝试的。 - Nikolai Samteladze
谢谢你的回答!奏效了。 - Tristan van Dam

1
这个答案是对SLaksnemesv评论的总结。
首先,没有理由嘲笑Func<IService>委托。相反,可以写成:
[TestMethod]
public void UnitTest()
{
    var mockService = new Mock<IService>();

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

    // The rest of the test
}

这个异常是因为Moq不支持这种情况。但是,它会抛出一个不太友好的InvalidCastException,而不是抛出NotSupportedException。

正如我在答案中所示,Moq确实支持此功能,InvalidCastException是因为该功能被错误地使用。 - Lukazoid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接