Can I exclude part of a method from code coverage?
Asked Answered
O

2

16

I suspect the answer is no, but I'll ask anyway...

TL;DR

I know I can exclude a class or method from coverage analysis with the [ExcludeFromCodeCoverage] attribute, but is there a way to exclude only part of a method?

Concrete example

I have a method that lazily generates a sequence of int.MaxValue elements:

private static IEnumerable<TElement> GenerateIterator<TElement>(Func<int, TElement> generator)
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        yield return generator(i);
    }
}

In practice, it's never fully enumerated, so the end of the method is never reached. Because of that, DotCover considers that 20% of the method is not covered, and it highlights the closing brace as uncovered (which corresponds to return false in the generated MoveNext method).

I could write a test that consumes the whole sequence, but it takes a very long time to run, especially with coverage enabled.

So I'd like to find a way to tell DotCover that the very last instruction doesn't need to be covered.

Note: I know I don't really need to have all the code covered by unit tests; some pieces of code can't or don't need to be tested, and I usually exclude those with the [ExcludeFromCodeCoverage] attribute. But I like to have 100% reported coverage for the code that I do test, because it makes it easier to spot untested parts of the code. Having a method with 80% coverage when you know there is nothing more to test in it is quite annoying...

Oireachtas answered 8/6, 2014 at 2:3 Comment(8)
Well, this method is static, but I'd think about finding a way to substitute int.MaxValue when you test with something manageable, unless that value is actually relevant somehow.Opinion
@AnthonyPegram, that's a good idea; the value isn't really relevant, I just want a sequence that is virtually infinite.Oireachtas
On the other hand, making the code more complex just to avoid an uncovered closing brace doesn't feel right...Oireachtas
Sure, I get the argument against over-engineering. Still, an internal static max counter property with a default value of MaxValue might help, and wouldn't be adding any real significant complexity. Then your tests can set up and tear down by setting a workable value and then resetting it, as applicable. (This would also require exposing internals to your unit testing project, if you haven't already.) Anyway, just a thought.Opinion
If you were to replace this with a deferred LINQ query, would that work too for code coverage? That is: return Enumerable.Range(0, int.MaxValue).Select(generator);? EDIT: Essentially, you'd "pass the buck" of the iteration/yielding itself to the LINQ code which wouldn't be counted by code coverage.Brandish
@AnthonyPegram, actually I took a different approach: I added an exclusiveUpperBound parameter to the GenerateIterator method. The public Generate method that calls it passes int.MaxValue, and the test directly calls the GenerateIterator (that I made internal) with another value. I think it's cleaner than mutable static state... Anyway, that solves the issue for this specific scenario, but not for the general case, so the question remains open ;)Oireachtas
I understand that. I personally don't write too many static methods, so I could just pass in a dependency to the class itself if I had to if testing was otherwise problematic (personal example: testing code that might assign the current date and time). Have the public constructor not need the dependency, with an internal constructor that accepts it (and the public constructor passes a default instance to the internal). Not quite the same approach here, but from your description, it seems close enough at the method level.Opinion
@ChrisSinclair, you're right! I don't know how I missed it; that's clearly the best way to do it. But anyway, the question still stands, because there are other scenarios where I would like to exclude parts of methods...Oireachtas
W
10

No, there is no way to exclude "part of a method" from coverage analysis with dotCover.

In the general sense you got a couple of options:

  1. Extract the uncovered part into its own method, so you can properly ignore that method from analsysis
  2. Ignore the problem

In this case there may be a third options. Since your test code exercises the majority of your method, perhaps you should just write a test method that makes sure the code runs to completion?

Wraparound answered 26/7, 2014 at 21:26 Comment(1)
I have an iterator that definitely runs to completion in a foreach loop in a unit test; it still doesn't show 100% coverage, not using dotCover, just the built in test explorer, and it highlights the code with the same effect, it shows the last two braces highlighted as not covered.Automaton
D
0

First and foremost, while "code coverage" can be an important metric, one must realize that it just might not be possible to have 100% "code coverage". 100% Code coverage is one of those metrics that you should aspire to attain, but which you never will; i.e. get as close as you possibly can.

OTOH, don't go crazy trying to get 100% code coverage. More importantly, is your code readable? Is it testable (I presume so since you're looking at code coverage)? Is it maintainable? Is it SOLID? Do you have passing unit, integration, and end-to-end tests? These things are more important than achieving 100% code coverage. What code coverage will tell you is how extensive your testing is (I'm not sure if the built-in code coverage analysis engine includes only unit tests, or includes all types of tests when calculating its statistics), which gives you an indication of whether or not you have enough tests. Also, while it will tell you how extensive your tests are (i.e. how many lines of code are executed by your tests), it won't tell you if your tests are any good (i.e. are your tests really testing what needs to be tested to ensure your application is working correctly).

Anyway, this may be not an answer, but food for thought.

Disdainful answered 26/7, 2014 at 21:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.