在 Visual Studio 2015 年从代码覆盖率中排除汽车属性

Exclude auto properties from Code Coverage in Visual Studio 2015

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

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

处理由此产生的所有误报实际上违背了代码覆盖率工具的目的,因为它几乎不可能识别缺少测试覆盖率的实际代码。我们不想为我们所有的 DTO 编写单元测试,我真的宁愿不必通过项目来注释每个 auto-属性 和 ExcludeFromCodeCoverage.

我在 https://github.com/iaingalloway/VisualStudioCodeCoverageIssue

创建了一个工作 MCVE


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

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

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

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

这不是一个很好的解决方法,但只要您不使用名称中带有 "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-03-18 更新:此版本使用反射在特定命名空间下查找您的模型。