I'm running into the following problem when migrating my app from ASP.NET Core 2.2 to ASP.NET Core 3.0:
I have a class which should log an error message under certain circumstances. This is done by calling LogError
on ILogger<MyClass>
.
I used to verifiy this with the following snippet from my unit test:
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>>())
);
Now here lies the problem:
In ASP.NET Core 2.2, the 3rd parameter (Mocked via It.IsAny<object>()
) was of an internal type FormattedLogValues
. This was a class, so It.IsAny<object>()
worked. In ASP.NET Core 3.0 it was changed to a struct, so It.IsAny<object>()
no longer matches it.
How can I get my Verify()
call to work in ASP.NET Core 3.0? Is there an It.IsAny()
version that matches any struct type?
Edit: Here is a fully runnable snippet that fails on ASP.NET Core 3.0 and succeeds on 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>>())
);
}
}
Changing It.IsAny<Func<object, Exception, string>>())
to (Func<object, Exception, string>) It.IsAny<object>()
seems to solve the problem. object
can also be replace by IsAnyType
if you're on Moq 4.13+.
Internally the Logger class uses FormattedLogValues
as the state parameter (the object
in my example). The change to struct seems to have something to do with it. What exactly the cause is I'm not sure. But there seems to be an issue on the Moq GitHub repo describing a few more details. There doesn't seem to be a concrete explanation yet why it used to work, but more info will probably be posted there soon.
https://github.com/moq/moq4/issues/918
I found the same problem in Github.
I made an extension method for the solution:
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With