迭代器块的奇怪测试覆盖率结果,为什么这些语句没有被执行? [英] Weird test coverage results for iterator block, why are these statements not executed?

查看:126
本文介绍了迭代器块的奇怪测试覆盖率结果,为什么这些语句没有被执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用dotCover来分析我的单元测试的代码覆盖率,我得到了一些奇怪的结果......我有一个迭代器方法,覆盖范围不完整,但是未涵盖的语句只是方法结束时的结束括号。

I'm using dotCover to analyze code coverage of my unit tests, and I'm getting some strange results... I have an iterator method for which the coverage is not complete, but the statements that are not covered are just the closing braces at the end of the method.

这是我正在测试的方法:

Here's the method I'm testing:

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

覆盖率结果:

我假设使用块关闭的大括号实际上是对枚举器的 Dispose 的调用;但是,为什么不执行?我首先怀疑NUnit没有处理调查员,但是如果我在实际上做一个foreach,我会得到相同的结果。

I assume the closing brace of the using block is actually the calls to Dispose on the enumerators; but then, why is it not executed? I first suspected that NUnit wasn't disposing the enumerators, but I get the same result if I do a foreach on actual.

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

As for the second uncovered closing brace, I have no idea what it stands for... I guess it's related to how the compiler transforms the iterator block.

任何人都可以了解这两个陈述是什么,为什么不执行?

Can anyone shed some light on what these two "statements" are, and why they are not executed ?

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

Peter raised a very good question: the results shown above were obtained when running the tests on a debug build. If I run the tests on a release build, coverage of the CommonPrefixImpl method is 100%, so it's probably related to compiler optimizations.

推荐答案

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

One of the problems with iterator methods is that the compiler generates a rather large and complex state machine to manage the deferred execution of the code within the iterator method. This usually generates a class or two. These classes are meant to deal with the general case and not your specific case, so there's likely at least a bit of code in there that is never used. You can look at what is generated by looking at your assembly with tools like ILSpy, JustDecompile or Reflector. It will show the classes in your assembly generated by the C# compiler (usually class names containing '<', etc.)

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

What the profiler knows about is how the PDB associates to your code and despite the possibility that all of the code you wrote possibly being executed, there's still a possibility that not all of the code generated by the compiler got executed. The profiler probably doesn't know this and simply says that a certain percentage (less than 100) of a particular iterator method got executed.

可能产生的一件事情是异常处理代码。因为编译器不知道你的代码不会或者可能不能生成异常,所以它仍然会生成代码以补偿异常 - 它需要保持它的状态不会变得腐败。我敢打赌,如果你在一个基于某个标志的迭代器方法中的各个地方引入一个异常的方法,并且运行该方法两次(一次没有异常,一次在同一次运行中有异常),百分比会有所不同 - 可能更高因为生成的异常处理代码会被执行。

One of the things likely getting generated is exception handling code. Because the compiler doesn't know your code won't or possibly can't generate an exception it will still generate code to compensate for an exception--it needs to keep it's state from becoming corrupt. I bet if you included a way to throw an exception in various places in your iterator method based on some flag and ran the method twice (once without exceptions and once with exceptions in the same run) that the percentages would be different--likely higher because the generated exception handling code would then be exercised.

方法似乎未执行的事实可能是因为该代码是执行状态机的不同方法,编译器永远不会生成从生成的代码到类中代码的关联。

The fact that the end of the method "seems" to not be executed is likely because that code is part of a different method in the state machine that gets executed and the compiler never generates an association from that generated code to the code in your class.

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

UPDATE: to get a better understanding of what the compiler is doing and see an example of the type of code it generates see section 10.14 Iterators in the C# spec (http://www.microsoft.com/en-us/download/details.aspx?id=7029)

这篇关于迭代器块的奇怪测试覆盖率结果,为什么这些语句没有被执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆