【问题标题】:Test not being picked up by code coverage (c# .net standard)代码覆盖率未获取测试(c# .net 标准)
【发布时间】:2018-10-18 14:10:36
【问题描述】:

我已经实现了自己的记录器,它遵循 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)覆盖。

知道如何正确执行此测试以便正确覆盖代码吗?我也在使用 起订量

问题是因为它是一个模拟实例吗?我不能模拟 TelemetryClient,因为我的另一个想法是测试这是 AppInsightsLogger 的一个实例,然后验证 TelemetryClient.log 被称为但它是一个密封的类,使这个测试变得复杂:-/

提前感谢任何提示! 注意:我对编写测试还很陌生 - 我了解这些概念,但没有实际执行。

【问题讨论】:

  • 代码覆盖率监控器是对的:你在模拟你正在测试的类,而实际的实现永远不会运行。与其模拟ILogger,不如在AppInsightsLogger 的真实实例上调用Log 方法,然后模拟TelemetryClient
  • TelemetryClient 虽然是一个密封类 :( 但这是有道理的,因为它是模拟实例 - 不知道如何测试这个
  • 将遥测客户端封装/包装在您控制的抽象后面,并将其注入记录器。记录器与实现问题的耦合过于紧密,因此难以单独进行单元测试。

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


【解决方案1】:

好吧,你的代码没有被覆盖,因为它没有被执行。您设置了一个模拟对象,然后在此设置上调用了一个方法。起订量不是您的真实代码,而是返回您设置的值。

所以当你打电话时:

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

简而言之:你不模拟你测试的类,你模拟这个类的依赖关系。这样当你测试你的类时,其他类的代码就不会运行,也不会影响你的测试结果。

【讨论】:

    猜你喜欢
    • 2018-03-24
    • 2019-01-18
    • 2015-03-04
    • 2013-09-09
    • 2015-01-12
    • 2015-11-22
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多