代码覆盖率未选取测试(c#.net标准)
我已经实现了我自己的记录器,它遵循ILogger接口。在内部,它有AppInsights客户端,这就是我的日志记录器实际记录的内容。这是我的课程的简化版本:代码覆盖率未选取测试(c#.net标准),c#,unit-testing,testing,moq,code-coverage,C#,Unit Testing,Testing,Moq,Code Coverage,我已经实现了我自己的记录器,它遵循ILogger接口。在内部,它有AppInsights客户端,这就是我的日志记录器实际记录的内容。这是我的课程的简化版本: public class AppInsightsLogger : ILogger { internal TelemetryClient Client { get; set; } public AppInsightsLogger(string instrumentationKey) { Telemet
public class AppInsightsLogger : ILogger
{
internal TelemetryClient Client { get; set; }
public AppInsightsLogger(string instrumentationKey)
{
TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey;
Client = new TelemetryClient();
}
public void Log<T>(LogLevel logLevel, EventId eventId, T state, Exception exception,
Func<T, Exception, string> formatter)
{
if (string.IsNullOrEmpty(eventId.Name))
{
StackFrame frame = new StackFrame(1);
var method = frame.GetMethod();
var type = method.DeclaringType;
var name = method.Name;
eventId = new EventId(eventId.Id == 0 ? BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0) : eventId.Id, $"{type}:{name}");
}
var props = new Dictionary<string, string>
{
{ "EventId", eventId.Id.ToString() },
{ "EventName", eventId.Name }
};
var telemetry = new EventTelemetry(eventId.Name);
if (!string.IsNullOrEmpty(state.ToString()))
{
telemetry.Properties.Add("SummaryMessage", state.ToString());
}
foreach (var property in props)
{
telemetry.Properties.Add(property);
}
Client.TrackEvent(telemetry);
}
}
但是当我运行这个测试时(通过得很好),我的代码覆盖率监视器(Jetbrain的dotCoverage)并没有覆盖它
你知道我如何正确地执行这个测试,从而正确地覆盖代码吗?我也在使用最小起订量
问题是因为它是一个模拟实例吗?我不能嘲笑TelemetryClient,因为我的另一个想法是测试AppInsightsLogger的实例,然后验证TelemetryClient.log是否被调用,但它是一个密封类,使测试复杂化:-/
提前感谢您提供的任何提示!!
注意:我对编写测试相当陌生-我理解这些概念,但没有实际执行。好吧,您的代码没有被涵盖,因为它没有被执行。您设置了一个模拟对象,然后在此设置上调用了一个方法。而不是你的真正的代码,moq踢进来,只是返回你设置的值 所以当你打电话时:
mock.Object.Log(LogLevel.Information, new EventId(1, "test"), "test", null, null);
它不会进入代码,它会在模拟对象上查找匹配的设置,并找到一个:
mock.Setup(m =>
m.Log<string>(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<string>(), null, null));
将其注入构造函数或属性中的记录器(使用DI):
最后在你的测试中:
var telemetryMock = new Mock<ITelemetryClientWrapper>();
var target = new MyLogger(telemetryMock.Object);
target.Log(...);
telemetryMock.Verify(c => c.TrackEvent());
var telemetryMock=new Mock();
var target=新的MyLogger(遥测模拟对象);
target.Log(…);
telemetryMock.Verify(c=>c.TrackEvent());
简而言之:您不模拟您测试的类,而是模拟该类的依赖关系。这样,当您测试类时,来自其他类的代码不会运行,也不会影响您的测试结果。好吧,您的代码没有被覆盖,因为它没有被执行。您设置了一个模拟对象,然后在此设置上调用了一个方法。而不是你的真正的代码,moq踢进来,只是返回你设置的值 所以当你打电话时:
mock.Object.Log(LogLevel.Information, new EventId(1, "test"), "test", null, null);
它不会进入代码,它会在模拟对象上查找匹配的设置,并找到一个:
mock.Setup(m =>
m.Log<string>(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<string>(), null, null));
将其注入构造函数或属性中的记录器(使用DI):
最后在你的测试中:
var telemetryMock = new Mock<ITelemetryClientWrapper>();
var target = new MyLogger(telemetryMock.Object);
target.Log(...);
telemetryMock.Verify(c => c.TrackEvent());
var telemetryMock=new Mock();
var target=新的MyLogger(遥测模拟对象);
target.Log(…);
telemetryMock.Verify(c=>c.TrackEvent());
简而言之:您不模拟您测试的类,而是模拟该类的依赖关系。因此,当您测试类时,来自其他类的代码不会运行,也不会影响您的测试结果。代码覆盖率监视器是正确的:您在模拟您正在测试的类,而实际实现永远不会运行。与其模拟
ILogger
,不如在AppInsightsLogger
的真实实例上调用Log方法,并模拟TelemetryClient
。TelemetryClient是一个密封类,但:(但这是有意义的,因为它是模拟实例-不确定如何测试它将遥测客户端封装/包装在您控制的抽象后面,并将其注入记录器中。记录器与实现问题紧密耦合,这使得很难单独进行单元测试。代码覆盖率监视器是正确的:您是检查您正在测试的类,而实际实现从未运行过。您要做的不是模拟ILogger
,而是在AppInsightsLogger
的真实实例上调用Log方法,并模拟TelemetryClient
。TelemetryClient是一个密封的类,但:(但这是有意义的,因为它是模拟实例-不确定如何测试它将遥测客户端封装/包装在您控制的抽象后面,并将其注入记录器中。记录器与实现问题紧密耦合,这使得很难单独进行单元测试。