Question

tl;dr... Code coverage reports 1 non-covered block with:

.Where(x => x.Field1.Contains(someValue) ||
            x.Field2.Contains(someValue))

Even though all of that code executes.


One of my methods has a .Where() predicate applied to an enumeration. Simplified, the method does this:

public IEnumerable<SomeType> Filter(IEnumerable<SomeType> source, string filter)
{
    return source.Where(x => x.SomeField.Contains(filter) ||
                             x.AnotherField.Contains(filter) ||
                             x.AThirdField.Contains(filter));
}

(There are actually 8 comparisons total in the original code.) My unit test passes a source with a handful of elements and a string that wouldn't be found in any of them, to validate that none are returned. The test passes, but the code coverage highlights the three lines in the predicate and says that there's one not-covered block.

What's not covered? Wouldn't all of this code execute in the test of it's indeed not finding any results? In the "hierarchy" column in Visual Studio's code coverage results, it lists this method as containing that one block:

<Filter>b__6(class SomeType)

At first I thought that maybe the compiled code is optimized should there be a field without a value, so that it never bothers with .Contains() on such a field. But I've confirmed that at least one element in source has all fields set with values, so I would think that all of the checks would be made.

What am I missing?


Edit: The non-contrived code, verbatim, is:

results = results.Where(o =>
    o.RemoteOccurrence.PatientName.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
    o.RemoteOccurrence.HealthID.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
    o.RemoteOccurrence.OccurrenceType.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
    o.RemoteOccurrence.PrimaryDiagnosis.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
    o.RemoteOccurrence.Description.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
    o.RemoteOccurrence.PrimaryClinician.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
    o.ReviewStatus.ToString().ToLowerInvariant().Contains(filter.ToLowerInvariant()));

The test data includes:

var entry = new ReviewOccurrence
{
    ReviewStatus = ReviewOccurrence.Status.New,
    RemoteOccurrence = new RemoteOccurrence
    {
        PatientName = "Jane Doe",
        HealthID = "12345",
        OccurrenceType = "ER to Admit Patient",
        PrimaryDiagnosis = "COPD",
        Description = "This is a test",
        PrimaryClinician = "Nurse Suzy"
    }
    // other unrelated fields
};

And the value of filter is "filter" which isn't in any of those fields.


Update:

I've taken the contrived example and created a runnable test from it, starting with only a single field to filter:

public class MyObject
{
    public string Name { get; set; }
}

public class MyWrapper
{
    private List<MyObject> objects = new List<MyObject>()
    {
        new MyObject { Name = "David" }
    };

    public IEnumerable<MyObject> Filter(string filter)
    {
        return objects.Where(o => o.Name.ToLowerInvariant().Contains(filter.ToLowerInvariant()));
    }
}

And the test:

[TestMethod]
public void TestMethod()
{
    var results = new MyWrapper().Filter("foo");
    Assert.AreEqual(0, results.Count());
}

Test passes, coverage is 100%. So I add another field to the object (call it Description) and add to the clause:

public IEnumerable<MyObject> Filter(string filter)
{
    return objects.Where(o => o.Name.ToLowerInvariant().Contains(filter.ToLowerInvariant()) ||
                              o.Description.ToLowerInvariant().Contains(filter.ToLowerInvariant()));
}

Test still passes, but now there's that 1 missing block of code coverage again. So it seems to be related to the chaining of clauses with the || operator between them. (Or at least the compiled result thereof.)

Continuing to tinker, I change it to this:

public IEnumerable<MyObject> Filter(string filter)
{
    return objects.Where(o => o.Name.ToLowerInvariant().Contains(filter.ToLowerInvariant()))
                  .Union(objects.Where(o => o.Description.ToLowerInvariant().Contains(filter.ToLowerInvariant())));
}

Test passes, back to 100% code coverage. Definitely the || clauses, but it's still not clear to me why.

No correct solution

OTHER TIPS

I suspect that the or condition is short circuiting because one of the earlier conditions evaluates to true and you aren't testing the remaining condition, thus not getting 100% code coverage. For example, you wouldn't get full coverage on the following:

var x = true;
var y = false;

var result = x || y;

In this case, when checking for result, x is evaluated, but y isn't because x already meets the or condition. If you really need 100% coverage, you need to test all possible permutations of or conditions (often a tedious exercise).

Maybe the contrived code could provide the answer you seek.

Create a separate solution which clone's the predicate scenario, but only in a strawman or very base way. Verify the code coverage is full and then begin to add conditions one by one, woodman, tinman, etc until either you have duplicated the situation or the clone is fully test covered and functional.

The difference if any found could lead you to why the originating code is not fully covered.


Update

What is code coverage? Something that Visual Studio informs us graphically on...hence we rely on it. But what if there is a bug in the code Visual Studio uses to determine the area of code coverage?


Negative Tests and Postive Tests Achieve 100%

I decided to write a connect bug with the scenario presented and began testing but with different situations. I changed the Filter code to or on these two competing conditions but where both would be hit:

public IEnumerable<MyObject> Filter(string filter)
{
    return objects.Where(o => o.Name.Contains(filter) ||
                            o.Name.Contains(filter) == false);
}

Then the first test where foo was not in any of the items so the second or condition would be hit

var results = new MyWrapper().Filter("foo");
Assert.IsTrue(results.Any());

I got the results of less than 100% code coverage as the OP got. But once I added a second test

var results = new MyWrapper().Filter(string.Empty);
Assert.IsTrue(results.Any());

I achieved 100%.

My conclusion is that in the above scenario described, one has not provided enough tests to in code coverage's algorithm's mind to achieve 100%.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top