使用 Moq 中的任何结构参数验证方法调用
Verifying method call with any struct parameter in Moq
将我的应用程序从 ASP.NET Core 2.2 迁移到 ASP.NET Core 3.0 时,我 运行 遇到以下问题:
我有一个 class 在某些情况下应该记录错误消息。这是通过在 ILogger<MyClass>
.
上调用 LogError
来完成的
我曾经用我的单元测试中的以下片段来验证这一点:
Mock<ILogger<MyClass>> loggerMock = ...;
MyClass myClass = ...;
myClass.MethodThatLogsTestException();
loggerMock.Verify(l => l.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<object>(),
It.IsAny<TestException>(),
It.IsAny<Func<object, Exception, string>>())
);
问题来了:
在 ASP.NET Core 2.2 中,第三个参数(通过 It.IsAny<object>()
模拟)是内部类型 FormattedLogValues
。这是 class,所以 It.IsAny<object>()
有效。在 ASP.NET Core 3.0 中,它被更改为结构,因此 It.IsAny<object>()
不再匹配它。
如何让我的 Verify()
调用在 ASP.NET Core 3.0 中工作?是否有匹配任何结构类型的 It.IsAny()
版本?
编辑:
这是一个完全可运行的片段,它在 ASP.NET Core 3.0 上失败并在 ASP.NET Core 2.2.
上成功
public class Test
{
public class Impl
{
private readonly ILogger<Impl> logger;
public Impl(ILogger<Impl> logger)
{
this.logger = logger;
}
public void Method()
{
logger.LogError(new Exception(), "An error occurred.");
}
}
[Fact]
public void LogsErrorOnException()
{
var loggerMock = new Mock<ILogger<Impl>>();
var sut = new Impl(loggerMock.Object);
sut.Method();
loggerMock.Verify(l => l.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<object>(),
It.IsAny<Exception>(),
It.IsAny<Func<object, Exception, string>>())
);
}
}
将 It.IsAny<Func<object, Exception, string>>())
更改为 (Func<object, Exception, string>) It.IsAny<object>()
似乎可以解决问题。如果您使用的是 Moq 4.13+,object
也可以替换为 IsAnyType
。
Logger class 在内部使用 FormattedLogValues
作为状态参数(在我的示例中为 object
)。结构的变化似乎与它有关。具体是什么原因我也不清楚。但是 Moq GitHub repo 上似乎有一个问题描述了一些更多的细节。似乎还没有具体的解释为什么它曾经有效,但可能很快就会发布更多信息。
我在 Github 中发现了同样的问题。
我对解决方案做了一个扩展方法:
public static void VerifyLog<T>(this Mock<ILogger<T>> mockLogger, Func<Times> times)
{
mockLogger.Verify(x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), times);
}
将我的应用程序从 ASP.NET Core 2.2 迁移到 ASP.NET Core 3.0 时,我 运行 遇到以下问题:
我有一个 class 在某些情况下应该记录错误消息。这是通过在 ILogger<MyClass>
.
LogError
来完成的
我曾经用我的单元测试中的以下片段来验证这一点:
Mock<ILogger<MyClass>> loggerMock = ...;
MyClass myClass = ...;
myClass.MethodThatLogsTestException();
loggerMock.Verify(l => l.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<object>(),
It.IsAny<TestException>(),
It.IsAny<Func<object, Exception, string>>())
);
问题来了:
在 ASP.NET Core 2.2 中,第三个参数(通过 It.IsAny<object>()
模拟)是内部类型 FormattedLogValues
。这是 class,所以 It.IsAny<object>()
有效。在 ASP.NET Core 3.0 中,它被更改为结构,因此 It.IsAny<object>()
不再匹配它。
如何让我的 Verify()
调用在 ASP.NET Core 3.0 中工作?是否有匹配任何结构类型的 It.IsAny()
版本?
编辑: 这是一个完全可运行的片段,它在 ASP.NET Core 3.0 上失败并在 ASP.NET Core 2.2.
上成功public class Test
{
public class Impl
{
private readonly ILogger<Impl> logger;
public Impl(ILogger<Impl> logger)
{
this.logger = logger;
}
public void Method()
{
logger.LogError(new Exception(), "An error occurred.");
}
}
[Fact]
public void LogsErrorOnException()
{
var loggerMock = new Mock<ILogger<Impl>>();
var sut = new Impl(loggerMock.Object);
sut.Method();
loggerMock.Verify(l => l.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<object>(),
It.IsAny<Exception>(),
It.IsAny<Func<object, Exception, string>>())
);
}
}
将 It.IsAny<Func<object, Exception, string>>())
更改为 (Func<object, Exception, string>) It.IsAny<object>()
似乎可以解决问题。如果您使用的是 Moq 4.13+,object
也可以替换为 IsAnyType
。
Logger class 在内部使用 FormattedLogValues
作为状态参数(在我的示例中为 object
)。结构的变化似乎与它有关。具体是什么原因我也不清楚。但是 Moq GitHub repo 上似乎有一个问题描述了一些更多的细节。似乎还没有具体的解释为什么它曾经有效,但可能很快就会发布更多信息。
我在 Github 中发现了同样的问题。
我对解决方案做了一个扩展方法:
public static void VerifyLog<T>(this Mock<ILogger<T>> mockLogger, Func<Times> times)
{
mockLogger.Verify(x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), times);
}