C# 自定义NLog滤波器类的单元测试
在实现自定义NLog筛选器时,我试图依赖C# 自定义NLog滤波器类的单元测试,c#,testing,nlog,C#,Testing,Nlog,在实现自定义NLog筛选器时,我试图依赖WhenMethodFilter,它接受lambda回调ShouldLogEvent。然而,要对回调lambda进行单元测试,我需要在生成配置的类中公开它,这并不理想——我讨厌仅仅为了测试而公开方法 private void ReconfigureNlog(object state) { var nlogConfig = ConstructNlogConfiguration(); foreach (var r
WhenMethodFilter
,它接受lambda回调ShouldLogEvent
。然而,要对回调lambda进行单元测试,我需要在生成配置的类中公开它,这并不理想——我讨厌仅仅为了测试而公开方法
private void ReconfigureNlog(object state)
{
var nlogConfig = ConstructNlogConfiguration();
foreach (var rule in nlogConfig.LoggingRules)
{
rule.Filters.Add(new WhenMethodFilter(ShouldLogEvent));
}
// TODO: maybe no need to reconfigure every time but just modify filter reference?
NLog.Web.NLogBuilder.ConfigureNLog(nlogConfig);
}
另一种方法是继承Filter
基类,并尝试用单元测试覆盖它。但问题是它没有公共接口:
internal FilterResult GetFilterResult(LogEventInfo logEvent)
protected abstract FilterResult Check(LogEventInfo logEvent);
这使得它也不可测试,除非我公开自己的方法。虽然这似乎是个小问题,但我很好奇是否有更好的办法。在我看来,将GetFilterResult
设置为内部是绝对没有必要的,尽管它有点遵循最佳设计实践。想法
更新1。待测试类别的代码:
public class TenancyLogFilter: Filter
{
private readonly ITenancyLoggingConfiguration _loggingConfigurationConfig;
private readonly IHttpContextAccessor _httpContextAccessor;
public TenancyLogFilter(ITenancyLoggingConfiguration loggingConfigurationConfig, IHttpContextAccessor httpContextAccessor)
{
_loggingConfigurationConfig = loggingConfigurationConfig;
_httpContextAccessor = httpContextAccessor;
}
protected override FilterResult Check(LogEventInfo logEvent)
{
var result = FilterResult.Neutral;
if (CanEmitEvent(logEvent.Level))
{
result = FilterResult.Log;
}
return result;
}
private LogLevel GetMinLogLevel()
{
var level = LogLevel.Trace;
if (!_loggingConfigurationConfig.TenantMinLoggingLevel.Any())
return level;
var context = _httpContextAccessor?.HttpContext;
if (context == null)
return level;
if (context.Request.Headers.TryGetValue(CustomHeaders.TenantId, out var tenantIdHeaders))
{
var currentTenant = tenantIdHeaders.First();
if (_loggingConfigurationConfig.TenantMinLoggingLevel.ContainsKey(currentTenant))
{
level = _loggingConfigurationConfig.TenantMinLoggingLevel[currentTenant];
}
}
return level;
}
private bool CanEmitEvent(LogLevel currentLogLevel)
{
return currentLogLevel >= GetMinLogLevel();
}
}
通常丑陋的技巧是将方法
设置为内部
,然后将其添加到主项目中的AssemblyInfo.cs中:
using System;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleToAttribute("Tenancy.UnitTests")]
然后,单元测试项目将被允许对主项目的内部方法进行单元测试
另请参见,以测试可以从subject类派生的类,从而公开调用被测成员所需的内容,因为目标成员受到保护
无法更改/访问内部成员,但在本例中,源代码显示它是一个简单的实现
/// <summary>
/// Gets the result of evaluating filter against given log event.
/// </summary>
/// <param name="logEvent">The log event.</param>
/// <returns>Filter result.</returns>
internal FilterResult GetFilterResult(LogEventInfo logEvent)
{
return Check(logEvent);
}
其实现将封装在自定义tenacyclogfilter
过滤器中所做的工作,并将其注入到目标类中
private readonly ILogEventAssessor service;
//...assumed injected service
private void ReconfigureNlog(object state) {
var nlogConfig = ConstructNlogConfiguration();
foreach (var rule in nlogConfig.LoggingRules) {
rule.Filters.Add(new WhenMethodFilter(ShouldLogEvent));
}
// TODO: maybe no need to reconfigure every time but just modify filter reference?
NLog.Web.NLogBuilder.ConfigureNLog(nlogConfig);
}
private FilterResult ShouldLogEvent(LogEventInfo logEvent) {
return service.GetFilterResult(logEvent);
}
//...
现在真的没有必要测试第三方过滤器来验证您的逻辑
你可以测试你的ILogEventAssessor
实现,以单独验证你的自定义逻辑。谢谢,我不知道,但可能不会这样做。是的,两者都可以,尽管拥有ILogEventAssessor
看起来只是为了使代码可测试而设计,感觉有点不必要。但我想这是一个偏好的问题。不管怎样,感谢您的详细解释,我个人更喜欢在测试的同时有一个测试类,它将公开测试。@Ivan在这种情况下,我同意这种方法更好。我将另一个作为替代方案,以表明有多种方法来解决这个问题。
public interface ILogEventAssessor {
FilterResult GetFilterResult(LogEventInfo logEvent);
}
private readonly ILogEventAssessor service;
//...assumed injected service
private void ReconfigureNlog(object state) {
var nlogConfig = ConstructNlogConfiguration();
foreach (var rule in nlogConfig.LoggingRules) {
rule.Filters.Add(new WhenMethodFilter(ShouldLogEvent));
}
// TODO: maybe no need to reconfigure every time but just modify filter reference?
NLog.Web.NLogBuilder.ConfigureNLog(nlogConfig);
}
private FilterResult ShouldLogEvent(LogEventInfo logEvent) {
return service.GetFilterResult(logEvent);
}
//...