C# 用假动作测试ILogger
我正在为自己做一个有趣的项目来学习blazor,很多核心概念和一般实践 我正在尝试为Fakeitesy实现记录器扩展,以便更容易地测试MicrosoftsiLogger。 基于.NET5,但现在正在努力使其适用于.NET5。 既然C# 用假动作测试ILogger,c#,fakeiteasy,ilogger,C#,Fakeiteasy,Ilogger,我正在为自己做一个有趣的项目来学习blazor,很多核心概念和一般实践 我正在尝试为Fakeitesy实现记录器扩展,以便更容易地测试MicrosoftsiLogger。 基于.NET5,但现在正在努力使其适用于.NET5。 既然FormattedLogValues是内部的,那么这个代码就不再工作了 我们的目标是在我的测试中有这样的东西(而不是使用Moq) \u logger.VerifyLog(LogLevel.Information,“调用天气”)。必须准确地执行() 或者A.CallTo(
FormattedLogValues
是内部的,那么这个代码就不再工作了
我们的目标是在我的测试中有这样的东西(而不是使用Moq)
\u logger.VerifyLog(LogLevel.Information,“调用天气”)。必须准确地执行()代码>
或者A.CallTo(()=>_logger.Log(LogLevel.Information,“Get Weather Called”)。musthavehappenedonecess();
它不必有语法上的糖分,但我想让它与FakeItEasy而不是Moq一起工作
下面是所有涉及到的代码。当然,我在第一行看到了一些不同之处Microsoft.Extensions.Logging.ILogger.Log
1[System.Object]vs
Microsoft.Extensions.Logging.ILogger1[Server.Controllers.WeatherForecastController]。Log
1[Microsoft.Extensions.Logging.formatedLogvalues]`但我不知道如何“修复”这是因为我不完全明白出了什么问题
我的问题很明显,我应该怎么做来修复它,但我也很好奇我遗漏了什么部分
我的错误如下:
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]
)
控制器:
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");
}
}
在阅读了这篇文章的评论后,我“明白”了问题所在。我浏览了无数的主题,提出了一个“可行”的解决方案,也许可以使用更好的编码标准。希望它能帮助其他人
编辑:一些解释
找到了作为解决方案基础的github注释。然后找到了github pr,其中他们提到要将Microsoft.Extensions.Logging.FormattedLogValues更改为IReadOnlyList
这可以从GetArgument(2)
中提取(来源:)
公共静态类LoggerExtensions
{
公共静态无效验证日志必须已发生(此ILogger记录器、日志级别、字符串消息)
{
尝试
{
logger.VerifyLog(级别、消息)
.一定发生过();
}
捕获(例外e)
{
使用消息“{message}\”,e验证对日志的调用时抛出新的ExpectationException($);
}
}
public static void VerifyLogMustNothaveOccessed(此ILogger记录器,日志级别,字符串消息)
{
尝试
{
logger.VerifyLog(级别、消息)
.mustnothavebefored();
}
捕获(例外e)
{
使用消息“{message}\”,e验证对日志的调用时抛出新的ExpectationException($);
}
}
公共静态IVoidArgumentValidationConfiguration VerifyLog(此ILogger记录器,日志级别,字符串消息)
{
返回A.CallTo(记录器)
.Where(call=>call.Method.Name==“Log”
&&call.GetArgument(0)=级别
&&CheckLogMessages(call.GetArgument(2),message));
}
私有静态bool CheckLogMessages(IReadOnlyList readonlyList,字符串消息)
{
foreach(ReadOnlyList中的var kvp)
{
if(kvp.Value.ToString()包含(消息))
{
返回true;
}
}
返回false;
}
}
您的错误是,格式化程序
委托(日志
方法中的最后一个参数)应该是Func
。但是您在这里使用的是Func
,它可能是issue@PavelAnikhouski谢谢你的解释。由于理解你评论中的错误,我做了一个变通。
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");
}
}
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
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)
.Where(call => call.Method.Name == "Log"
&& call.GetArgument<LogLevel>(0) == level
&& CheckLogMessages(call.GetArgument<IReadOnlyList<KeyValuePair<string, object>>>(2), message));
}
private static bool CheckLogMessages(IReadOnlyList<KeyValuePair<string, object>> readOnlyLists, string message)
{
foreach(var kvp in readOnlyLists)
{
if (kvp.Value.ToString().Contains(message))
{
return true;
}
}
return false;
}
}