我已经实现了自己的记录器,该记录器遵循ILogger界面。在内部,它具有AppInsights客户端,这是我的记录器实际登录的内容。这是我班的简化版本:
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);
}
}
我要验证的内容(可能是错误的,也请告知),是当我调用AppInsightsLogger
类Log
方法时,它实际上试图调用TelemetryClient的TrackEvent方法。我在考虑这样的测试:
[Fact, IsUnit]
public void Test_Telemetry_Log()
{
var mock = new Mock<ILogger>();
mock.Setup(m =>
m.Log<string>(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<string>(), null, null));
mock.Object.Log(LogLevel.Information, new EventId(1, "test"), "test", null, null);
mock.Verify();
}
但是,当我运行此测试时(可以通过),它没有显示在代码覆盖率监视器(Jetbrain的 dotCoverage )所涵盖的范围内。
有什么想法可以正确执行此测试,以便正确覆盖代码吗?我也在使用 Moq 。
出现问题是因为它是一个模拟实例?我无法嘲笑TelemetryClient,因为我想测试它的另一个想法是AppInsightsLogger的实例,然后验证TelemetryClient.log被称为BUT,这是一个密封的类,它使该测试复杂化:-/
在此先感谢您的提示!! 注意:我对编写测试还很陌生-我了解概念,但实际上并不执行。
答案 0 :(得分:2)
好吧,您的代码没有被覆盖,因为它没有被执行。您设置了一个模拟对象,然后在此设置上称为方法。 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));
实际上,在测试中,您应该实例化AppInsightsLogger并在其上调用登录。将TelemetryClient隐藏在界面后面,对其进行模拟,然后进行验证。不赘述,看起来会像这样:
public interface ITelemetryClientWrapper{
void TrackEvent();
}
public class MyTelemetryClient : ITelemetryClientWrapper{
public void TrackEvent(){
// Use real TelemetryClient here, I guess it's not your class, right? If it's your class, just extract an interface from it
}
}
将其注入到构造函数或属性中的Logger中(使用DI):
public class MyLogger{
public MyLogger(ITelemetryClientWrapper wrapper){
}
}
最后在您的测试中:
var telemetryMock = new Mock<ITelemetryClientWrapper>();
var target = new MyLogger(telemetryMock.Object);
target.Log(...);
telemetryMock.Verify(c => c.TrackEvent());
简而言之:您不模拟测试的类,而是模拟此类的依赖项。这样一来,当您测试类时,其他类的代码将不会运行,也不会影响测试结果。