【问题标题】:Exclude auto properties from Code Coverage in Visual Studio 2015从 Visual Studio 2015 的代码覆盖率中排除自动属性
【发布时间】:2015-09-01 10:35:01
【问题描述】:

我刚刚将一堆项目升级到 VS2015/C#6。

现在 MSTest 的代码覆盖率分析报告说单元测试未涵盖某些自动属性。在 Visual Studio 2013 中并非如此,我怀疑这可能与 C#6 中的新自动属性功能有关。

处理由此产生的所有误报反而违背了代码覆盖率工具的目的,因为它实际上无法识别缺乏测试覆盖率的实际代码。我们不想为我们所有的 DTO 编写单元测试,我真的不想在项目中用ExcludeFromCodeCoverage 注释每个自动属性。

我在https://github.com/iaingalloway/VisualStudioCodeCoverageIssue创建了一个工作MCVE


  • 在 Visual Studio 2013 Premium 或 Ultimate 中打开 VisualStudio2013.sln
  • 点击测试 -> 分析代码覆盖率 -> 所有测试。
  • 观察“代码覆盖结果”窗口报告 0 个块“未覆盖”。

  • 在 Visual Studio 2015 Enterprise 中打开 VisualStudio2015.sln
  • 点击测试 -> 分析代码覆盖率 -> 所有测试。
  • 观察“代码覆盖结果”窗口报告 1 个“未覆盖”块(ExampleDto.Value 的 getter)

是否可以将 Visual Studio 2015 中的内置代码覆盖工具配置为像 Visual Studio 2013 那样忽略自动属性?

【问题讨论】:

  • 这种行为似乎是 Visual Studio 2015 中的一个错误。目前除了使用 [ExcludeFromCodeCoverage] 之外没有其他解决方法。您可以通过以下方式监控工单的进度:- connect.microsoft.com/VisualStudio/Feedback/Details/1742106
  • 问题是你有一个类的属性没有被任何单元测试代码触及。它们是未使用的代码。你应该问的问题是为什么你有这些属性?如果他们有一个好的答案,请务必使用[Exclude...] 属性,但要立即使用// reason why each can be excluded

标签: c# visual-studio-2015 code-coverage mstest c#-6.0


【解决方案1】:

作为一种解决方法,您可以将以下内容添加到您的 .runsettings 文件中:-

<RunSettings>
  <DataCollectionRunSettings>
    <DataCollector ...>
      <Configuration>
        <CodeCoverage>
          <Functions>
            <Exclude>
              <Function>.*get_.*</Function>
              <Function>.*set_.*</Function>
            </Exclude>
          ...

这不是一个很好的解决方法,但只要您不使用名称中带有“get_”或“set_”的任何函数,它应该可以为您提供所需的行为。

【讨论】:

    【解决方案2】:

    我不喜欢过滤所有的 get/set 方法,尤其是因为我有时会编写需要测试的 get 和 set 逻辑。对我来说,对于相对简单的模型的基本覆盖,以下一对 xUnit 测试运行良好:

        public class ModelsGetSetTest
        {
            [ClassData(typeof(ModelTestDataGenerator))]
            [Theory]
            public void GettersGetWithoutError<T>(T model)
            {
                var properties =
                    typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                for (var i = 0; i < properties.Length; i++)
                {
                    var prop = properties[i];
                    if (prop.GetGetMethod(true) != null)
                        prop.GetValue(model);
                }
            }
    
            [ClassData(typeof(ModelTestDataGenerator))]
            [Theory]
            public void SettersSetWithoutError<T>(T model)
            {
                var properties =
                    typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                for (var i = 0; i < properties.Length; i++)
                {
                    var prop = properties[i];
                    if (prop.GetSetMethod(true) != null)
                        prop.SetValue(model, null);
                }
            }
    
            public class ModelTestDataGenerator : IEnumerable<object[]>
            {
                private readonly List<object[]> _data = new List<object[]>();
    
                public ModelTestDataGenerator()
                {
                    var assembly = typeof(Program).Assembly;
                    var nsprefix = $"{typeof(Program).Namespace}.{nameof(Models)}";
                    var modelTypes = assembly.GetTypes()
                        .Where(t => t.IsClass && !t.IsGenericType) // can instantiate without much hubbub
                        .Where(t => t.Namespace.StartsWith(nsprefix)) // is a model
                        .Where(t => t.GetConstructor(Type.EmptyTypes) != null) // has parameterless constructor
                        .ToList();
                    foreach (var modelType in modelTypes) _data.Add(new[] {Activator.CreateInstance(modelType)});
                }
    
                public IEnumerator<object[]> GetEnumerator()
                {
                    return _data.GetEnumerator();
                }
    
                IEnumerator IEnumerable.GetEnumerator()
                {
                    return GetEnumerator();
                }
            }
        }
    

    2020 年 3 月 18 日更新:此版本使用反射在特定命名空间下查找您的模型。

    【讨论】:

      【解决方案3】:

      我认为[ExcludeFromCodeCoverage] 是您唯一的选择。这只是你必须要做的一次性事情。我个人会在属性 getter/setter 上编写单元测试,尤其是在 WPF 中,我想确保属性更改通知发生。

      【讨论】:

      • 如果您在 getter/setter 中有逻辑(例如属性更改通知),那是一回事。如果它只是一个自动属性,那就完全是另一回事了。 int Foo { get; set; }。这个代码库中有数千个。代码分析中几乎 18% 的“块”,并且每次都在添加更多。 必须有更好的方法!
      • @Iain 似乎与 MSTest 没有任何关系。有关可以在 VS2015 中排除的内容的详细信息,请参阅 msdn.microsoft.com/en-us/library/jj159530.aspx
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-16
      • 1970-01-01
      相关资源
      最近更新 更多