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.