Tho*_*que 13 c# unit-testing iterator code-coverage dotcover
我正在使用dotCover来分析我的单元测试的代码覆盖率,并且我得到了一些奇怪的结果......我有一个迭代器方法,覆盖范围不完整,但是未涵盖的语句只是结束括号在方法的最后.
这是我正在测试的方法:
public static IEnumerable<T> CommonPrefix<T>(
this IEnumerable<T> source,
IEnumerable<T> other,
IEqualityComparer<T> comparer)
{
source.CheckArgumentNull("source");
other.CheckArgumentNull("other");
return source.CommonPrefixImpl(other, comparer);
}
private static IEnumerable<T> CommonPrefixImpl<T>(
this IEnumerable<T> source,
IEnumerable<T> other,
IEqualityComparer<T> comparer)
{
comparer = comparer ?? EqualityComparer<T>.Default;
using (IEnumerator<T> en1 = source.GetEnumerator(),
en2 = other.GetEnumerator())
{
while (en1.MoveNext() && en2.MoveNext())
{
if (comparer.Equals(en1.Current, en2.Current))
yield return en1.Current;
else
yield break;
}
} // not covered
} // not covered
Run Code Online (Sandbox Code Playgroud)
单元测试:
[Test]
public void Test_CommonPrefix_SpecificComparer()
{
var first = new[] { "Foo", "Bar", "Baz", "Titi", "Tata", "Toto" };
var second = new[] { "FOO", "bAR", "baz", "tata", "Toto" };
var expected = new[] { "Foo", "Bar", "Baz" };
var actual = first.CommonPrefix(second, StringComparer.CurrentCultureIgnoreCase);
Assert.That(actual, Is.EquivalentTo(expected));
}
Run Code Online (Sandbox Code Playgroud)
覆盖率结果如下:

我假设using块的右括号实际上是Dispose对枚举器的调用; 但是,为什么它没有被执行?我首先怀疑NUnit没有处理调查员,但是如果我做一个foreach,我会得到相同的结果actual.
至于第二个未覆盖的右括号,我不知道它代表什么......我猜这与编译器如何转换迭代器块有关.
任何人都可以了解这两个"陈述"是什么,为什么它们没有被执行?
编辑:Peter提出了一个非常好的问题:上面显示的结果是在调试版本上运行测试时获得的.如果我在发布版本上运行测试,则该CommonPrefixImpl方法的覆盖率为100%,因此它可能与编译器优化有关.
Pet*_*hie 12
迭代器方法的一个问题是编译器生成一个相当大且复杂的状态机来管理迭代器方法中代码的延迟执行.这通常会产生一两个类.这些类是为了处理一般情况而不是你的特定情况,因此可能至少有一些从未使用过的代码.您可以通过使用ILSpy,JustDecompile或Reflector等工具查看程序集来查看生成的内容.它将显示由C#编译器生成的程序集中的类(通常包含'<'的类名等)
探查器知道的是PDB如何与您的代码关联,尽管您编写的所有代码都可能被执行,但仍有可能并非所有编译器生成的代码都被执行.分析器可能不知道这一点,只是说某个特定迭代器方法的某个百分比(小于100)被执行了.
可能产生的一件事是异常处理代码.因为编译器不知道你的代码不会或者可能不能生成异常,所以它仍然会生成代码来补偿异常 - 它需要保持它的状态不被破坏.我敢打赌,如果你包含一种方法在基于某个标志的迭代器方法中的各个位置抛出一个异常并且运行该方法两次(一次没有异常,一次在同一次运行中有异常),百分比会有所不同 - 可能更高因为生成的异常处理代码将被执行.
"似乎"未执行的方法结束的事实可能是因为该代码是状态机中执行的不同方法的一部分,并且编译器从不生成从生成的代码到类中的代码的关联.
更新:为了更好地理解编译器正在做什么,并查看它生成的代码类型的示例,请参阅C#规范中的第10.14节迭代器(http://www.microsoft.com/en-us/download/details) .aspx?id = 7029)