c# - 迭代器 block 的奇怪测试覆盖率结果,为什么不执行这些语句?

标签 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

单元测试:

    [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));
    }

以及覆盖结果:

coverage results

我假设 using block 的右大括号实际上是对枚举器上的 Dispose 的调用;但是,为什么不执行呢?我首先怀疑 NUnit 没有处理枚举器,但如果我在 actual 上执行 foreach,我会得到相同的结果。

至于第二个未覆盖的右大括号,我不知道它代表什么......我猜它与编译器如何转换迭代器 block 有关。

谁能阐明这两个“语句”是什么,以及为什么它们没有被执行?


编辑:Peter 提出了一个很好的问题:上面显示的结果是在调试构建 上运行测试时获得的。如果我在发布版本 上运行测试,CommonPrefixImpl 方法的覆盖率为 100%,因此它可能与编译器优化有关。

最佳答案

迭代器方法的一个问题是编译器会生成一个相当大且复杂的状态机来管理迭代器方法中代码的延迟执行。这通常会生成一两个类。这些类旨在处理一般情况而不是您的特定情况,因此其中可能至少有一些代码从未使用过。您可以通过使用 ILSpy、JustDecompile 或 Reflector 等工具查看程序集来查看生成的内容。它将显示 C# 编译器生成的程序集中的类(通常类名包含“<”等)

探查器知道的是 PDB 如何与您的代码相关联,尽管有可能 您编写的所有代码 都可能被执行,但仍有可能并非所有由生成的代码编译器被执行了。探查器可能不知道这一点,只是说执行了特定百分比(小于 100)的特定迭代器方法。

可能生成的内容之一是异常处理代码。因为编译器不知道您的代码不会或可能不会生成异常,所以它仍会生成代码来补偿异常——它需要防止其状态被破坏。我敢打赌,如果你在迭代器方法的不同地方包含了一种方法,可以根据某个标志抛出异常,并运行该方法两次(一次没有异常,一次在同一次运行中有异常),那么百分比会不同——可能更高因为随后将执行生成的异常处理代码。

方法的结尾“似乎”没有被执行的事实可能是因为该代码是状态机中被执行的不同方法的一部分,并且编译器从不生成从生成的代码到代码的关联在你的类里面。

更新:要更好地了解编译器正在做什么并查看它生成的代码类型的示例,请参阅 C# 规范中的 10.14 迭代器部分( http://www.microsoft.com/en-us/download/details.aspx?id=7029 )

关于c# - 迭代器 block 的奇怪测试覆盖率结果,为什么不执行这些语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11918858/

相关文章:

c# - C# 控制台应用程序中的 SQL 更新错误

c# - 在 C# 中检测多个同时按键

c# - 应用 CQRS - 是否需要对薄读取层进行单元测试?

java - 当前修改异常 Android 迭代器

c++ - 访问 OpenCV Mat 元素时确定模板类型

c# - 在 UWP 项目中引用 native c++ dll

c# - LINQ根据条件JOIN不同的表

javascript - react 测试错误。目标容器不是 DOM 元素

c# - 单元测试 - 排列测试项目无法访问的对象

c++ - 如何从 int* 类型的堆分配数组中检索 int[num_elem] 类型的变量?