代码覆盖率未检测到测试(C#.net标准)

时间:2018-10-18 14:10:37

标签: c# unit-testing testing moq code-coverage

我已经实现了自己的记录器,该记录器遵循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);
    }
}

我要验证的内容(可能是错误的,也请告知),是当我调用AppInsightsLoggerLog方法时,它实际上试图调用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 )所涵盖的范围内。

enter image description here

有什么想法可以正确执行此测试,以便正确覆盖代码吗?我也在使用 Moq

出现问题是因为它是一个模拟实例?我无法嘲笑TelemetryClient,因为我想测试它的另一个想法是AppInsightsLogger的实例,然后验证TelemetryClient.log被称为BUT,这是一个密封的类,它使该测试复杂化:-/

在此先感谢您的提示!! 注意:我对编写测试还很陌生-我了解概念,但实际上并不执行。

1 个答案:

答案 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());

简而言之:您不模拟测试的类,而是模拟此类的依赖项。这样一来,当您测试类时,其他类的代码将不会运行,也不会影响测试结果。