我正在为自己做一个有趣的项目来学习 blazor、许多核心概念和一般实践。
我正在尝试为 FakeItEasy 实现记录器扩展,以便更轻松地测试 Microsoft 的 ILogger。基于https://dev.azure.com/BrideButler/Bride%20Butler%20Open%20Source/_git/FakeItEasy.DotnetCore.LoggerExtensions?path=%2FFakeItEasy.DotnetCore.LoggerExtensions%2FLoggerExtensions.cs但现在正试图让它工作对于.NET 5。现在这FormattedLogValues
是内部代码不再起作用了。
目标是在我的测试中有这样的东西(而不是使用 Moq)
_logger.VerifyLog(LogLevel.Information, "Get Weather Called").MustHaveHappenedOnceExactly();
或者A.CallTo(() => _logger.Log(LogLevel.Information, "Get Weather Called").MustHaveHappenedOnceExactly();
它不必有语法糖,但我想让它与 FakeItEasy 而不是 Moq 一起工作。
以下是所有涉及的代码。Microsoft.Extensions.Logging.ILogger.Log
当然,我在第一行1[System.Object] vs
Microsoft.Extensions.Logging.ILogger 1[Microsoft.Extensions.Logging.FormattedLogValues]`中看到了一些差异,1[Server.Controllers.WeatherForecastController].Log
但我不知道如何“解决”这个问题,因为我不完全理解出了什么问题。
我的问题显然是,我应该怎么做才能解决它,但也很好奇我缺少什么部分。
我的错误如下:
Assertion failed for the following call:
Microsoft.Extensions.Logging.ILogger.Log`1[System.Object]
(
logLevel: Information,
eventId: <Ignored>,
state: <e => e.ToString().Contains(value(Server.Tests.Extensions.LoggerExtensions+<>c__DisplayClass2_0`1[Server.Controllers.WeatherForecastController]).message)>,
exception: <Ignored>,
formatter: <Ignored>
)
Expected to find it once exactly but didn't find it among the calls:
1: Microsoft.Extensions.Logging.ILogger`1[Server.Controllers.WeatherForecastController].Log`1[Microsoft.Extensions.Logging.FormattedLogValues]
(
logLevel: Information,
eventId: 0,
state: [[{OriginalFormat}, Get Weather Called]],
exception: NULL,
formatter: System.Func`3[Microsoft.Extensions.Logging.FormattedLogValues,System.Exception,System.String]
)
测试:(使用 xUnit、FakeItEasy、Fixture)
记录器扩展
public static class LoggerExtensions
{
public static void VerifyLogMustHaveHappened<T>(this ILogger<T> logger, LogLevel level, string message)
{
try
{
logger.VerifyLog(level, message)
.MustHaveHappened();
}
catch (Exception e)
{
throw new ExpectationException($"while verifying a call to log with message: \"{message}\"", e);
}
}
public static void VerifyLogMustNotHaveHappened<T>(this ILogger<T> logger, LogLevel level, string message)
{
try
{
logger.VerifyLog(level, message)
.MustNotHaveHappened();
}
catch (Exception e)
{
throw new ExpectationException($"while verifying a call to log with message: \"{message}\"", e);
}
}
public static IVoidArgumentValidationConfiguration VerifyLog<T>(this ILogger<T> logger, LogLevel level,
string message)
{
return A.CallTo(() => logger.Log(
level,
A<EventId>._,
A<object>.That.Matches(e => e.ToString().Contains(message)),
A<Exception>._,
A<Func<object, Exception, string>>._)
);
}
}
测试构造函数:
private readonly IFixture _fixture;
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastControllerTests()
{
_fixture = new Fixture().Customize(new AutoFakeItEasyCustomization());
_logger = _fixture.Freeze<Fake<ILogger<WeatherForecastController>>>().FakedObject;
}
测试:
[Fact]
public void WeatherForecast_Get_Should_Log()
{
// Arrange
var weatherController = new WeatherForecastController(_logger);
// Act
weatherController.Get();
// Assert
_logger.VerifyLog(LogLevel.Information, "Get Weather Called").MustHaveHappenedOnceExactly();
}
控制器:
public class WeatherForecastController : ControllerBase
{
private readonly ILogger _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
// Left out other code then logging
_logger.Log(LogLevel.Information, "Get Weather Called");
}
}
更新: 找到的选项
仅测试是否使用正确的日志级别调用了 logger:
public static IVoidArgumentValidationConfiguration VerifyLog<T>(this ILogger<T> logger, LogLevel level, string message)
{
return A.CallTo(logger).Where(call => call.Method.Name == "Log" && call.GetArgument<LogLevel>(0) == level);
}
切换到最小起订量:
https://stackoverflow.com/a/59182490/1112413
https://stackoverflow.com/a/56728528/1112413