Moq验证方法签名可以看到调用,但调用不匹配?

时间:2019-01-11 16:20:26

标签: c# unit-testing moq

我有一个像这样的界面:

public interface IStatisticsCollector : IDisposable
{
    Task Measure(string metricName, decimal value, IDictionary<string, string> tags = null);
}

我正在将此IStatisticsCollector注入我的班级,并像这样使用它:

var stopwatch = new Stopwatch();
await dataCollector.Measure("rbk_init", stopwatch.ElapsedMilliseconds);
...
await dataCollector.Measure("rbk_compiled", stopwatch.ElapsedMilliseconds);
...

设置我的单元测试以验证我正在记录我想要的所有统计点,我对IStatisticsCollector进行了模拟:

private readonly Mock<IStatisticsCollector> _statisticsCollector = new Mock<IStatisticsCollector>();
_statisticsCollector.Setup(x => x.Measure(It.IsAny<string>(), It.IsAny<long>(), It.IsAny<IDictionary<string, string>>())).Verifiable();

运行单元测试时,我的验证在此行失败:

//assert
_statisticsCollector.Verify(
    x => x.Measure(It.IsAny<string>(), It.IsAny<long>(), It.IsAny<IDictionary<string, string>>()), Times.Exactly(5));

...并显示以下消息:

Moq.MockException : 
Expected invocation on the mock exactly 5 times, but was 0 times: x => x.Measure(It.IsAny<string>(), (decimal)It.IsAny<long>(), It.IsAny<IDictionary<string, string>>())

Configured setups: 
IStatisticsCollector x => x.Measure(It.IsAny<string>(), (decimal)It.IsAny<long>(), It.IsAny<IDictionary<string, string>>())

Performed invocations: 
IStatisticsCollector.Measure("rbk_init", 31, null)
IStatisticsCollector.Measure("rbk_compiled", 35, null)
IStatisticsCollector.Measure("rbk_stored", 36, null)
IStatisticsCollector.Measure("rbk_db_updated", 352, null)
IStatisticsCollector.Measure("rbk_completed", 361, null)
   at Moq.Mock.VerifyCalls(Mock targetMock, InvocationShape expectation, LambdaExpression expression, Times times, String failMessage) in C:\projects\moq4\src\Moq\Mock.cs:line 378

...这很奇怪,因为看起来它捕获了5个匹配的调用,但显然并不认为它们实际上是匹配的。现在我推测这可能与以下事实有关:秒表的ElapsedMilliseconds很长,但是接口期望的是小数(带有隐式强制转换),因此我更改了校验以查找It.IsAny<decimal>(),但是那给了我一个意想不到的结果:

Moq.MockException : 
Expected invocation on the mock exactly 5 times, but was 1 times: x => x.Measure(It.IsAny<string>(), It.IsAny<decimal>(), It.IsAny<IDictionary<string, string>>())

Configured setups: 
IStatisticsCollector x => x.Measure(It.IsAny<string>(), It.IsAny<decimal>(), It.IsAny<IDictionary<string, string>>())

Performed invocations: 
IStatisticsCollector.Measure("rbk_init", 28, null)
   at Moq.Mock.VerifyCalls(Mock targetMock, InvocationShape expectation, LambdaExpression expression, Times times, String failMessage) in C:\projects\moq4\src\Moq\Mock.cs:line 378

发现一个...只有一个。似乎没有抛出异常,所以我不知道为什么只有一个例外。

我还注意到了可选参数上的空值,并尝试针对空值而不是IDictionary<string, string>进行验证,但这同样无济于事。

有人可以解释这种行为吗?我该怎么做才能修复测试?

1 个答案:

答案 0 :(得分:3)

此行将始终失败:

_statisticsCollector.Verify(
    x => x.Measure(It.IsAny<string>(), It.IsAny<long>(), It.IsAny<IDictionary<string, string>>()), Times.Exactly(5));

之所以会发生这种情况,是因为您对模拟程序说它需要在第二个参数中接收一个long,但是接口说在第二个参数中它将接收一个十进制:

public interface IStatisticsCollector : IDisposable
{
    Task Measure(string metricName, decimal value, IDictionary<string, string> tags = null);
}

由于小数不能太长,因此验证将始终失败。