Verifying method call with any struct parameter in Moq

扶醉桌前 提交于 2020-08-24 09:04:07

问题


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>>())
        );
    }
}

回答1:


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




回答2:


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);
}


来源:https://stackoverflow.com/questions/57989318/verifying-method-call-with-any-struct-parameter-in-moq

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!