在 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 2013 Premium 或 Ultimate 中打开
VisualStudio2013.sln
。
- 单击测试 -> 分析代码覆盖率 -> 所有测试。
- 观察 "Code Coverage Results" window 报告 0 个块 "Not Covered"。
- 在 Visual Studio 2015 Enterprise 中打开
VisualStudio2015.sln
。
- 单击测试 -> 分析代码覆盖率 -> 所有测试。
- 观察 "Code Coverage Results" window 报告 1 个块 "Not Covered"(getter 为 ExampleDto.Value)
是否可以在 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 更新:此版本使用反射在特定命名空间下查找您的模型。
我刚刚将一堆项目升级到 VS2015/C#6.
现在 MSTest 的代码覆盖率分析报告单元测试未涵盖某些汽车属性。在 Visual Studio 2013 中情况并非如此,我怀疑这可能与 C#6 中新的 auto属性 功能有关。
处理由此产生的所有误报实际上违背了代码覆盖率工具的目的,因为它几乎不可能识别缺少测试覆盖率的实际代码。我们不想为我们所有的 DTO 编写单元测试,我真的宁愿不必通过项目来注释每个 auto-属性 和 ExcludeFromCodeCoverage
.
我在 https://github.com/iaingalloway/VisualStudioCodeCoverageIssue
创建了一个工作 MCVE- 在 Visual Studio 2013 Premium 或 Ultimate 中打开
VisualStudio2013.sln
。 - 单击测试 -> 分析代码覆盖率 -> 所有测试。
- 观察 "Code Coverage Results" window 报告 0 个块 "Not Covered"。
- 在 Visual Studio 2015 Enterprise 中打开
VisualStudio2015.sln
。 - 单击测试 -> 分析代码覆盖率 -> 所有测试。
- 观察 "Code Coverage Results" window 报告 1 个块 "Not Covered"(getter 为 ExampleDto.Value)
是否可以在 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 更新:此版本使用反射在特定命名空间下查找您的模型。