When statements that include chained method calls are formatted across multiple lines only the first line is identified as covered, other lines are reported as not covered, which in turn affects coverage metrics calculations.
I have the following in a C# class
var errorMessages = exception.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
In the editor the SelectMany and Select parts of the statement have a dot indicating the line is not covered, and the metrics window shows the file as being only 95.53% covered
If I reformat the statement so that it is on a single line, the line is marked as covered in the editor and the metrics window shows the file as being 100% covered.
I use ReSharper CleanUp extensively and have it configured to format chained method calls across multiple lines. I personally find this more readable.
I feel NCrunch should recognise this as a single statement, at least for metrics reporting.
Mike Hanson
Daily Usage Issues
Chained Method Calls Across Multiple Lines - Not Covered
Started by MikeHanson on 5,302 views
Remco NCrunch Developer
#7401
08 Jun 2015 11:37 UTC
Hi Mike,
Thanks for sharing this.
The uncovered lines in this case are identified as code by NCrunch, but because they are implemented using IEnumerable, they won't be executed unless you actually enumerate the errorMessages.
Try adding a .ToList() or .ToArray() to see if this makes any difference.
Cheers,
Remco
Thanks for sharing this.
The uncovered lines in this case are identified as code by NCrunch, but because they are implemented using IEnumerable, they won't be executed unless you actually enumerate the errorMessages.
Try adding a .ToList() or .ToArray() to see if this makes any difference.
Cheers,
Remco
Thanks for the quick response Remco.
I understand better now why this occurs, but that suggests there might be something else not quite right.
The very next statement after the aforementioned one is:
var fullErrorMessage = string.Join("; ", errorMessages);
And this statement is recognised as fully covered.
The full method shown below, is a private member of a Repository class and it simply wraps a call to the SaveChanges method of an internal DbContext. All the method does is provide a more informative message, when DbEntityValidationException is caught.
private void SaveChanges()
{
try
{
this.context.SaveChanges();
}
catch (DbEntityValidationException exception)
{
var errorMessages = exception.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var exceptionMessage = string.Concat(exception.Message, " The validation errors are: ", fullErrorMessage);
throw new DbEntityValidationException(exceptionMessage, exception.EntityValidationErrors);
}
}
The only two lines that are flagged as not covered are the Select* lines. In this case I can "fix" the coverage stats by restoring it to a single line (the chances of this class being changed again are very small), which will please the people complaining about the total coverage on the solution being below 95%, but it would be interesting to understand more.
Mike
I understand better now why this occurs, but that suggests there might be something else not quite right.
The very next statement after the aforementioned one is:
var fullErrorMessage = string.Join("; ", errorMessages);
And this statement is recognised as fully covered.
The full method shown below, is a private member of a Repository class and it simply wraps a call to the SaveChanges method of an internal DbContext. All the method does is provide a more informative message, when DbEntityValidationException is caught.
private void SaveChanges()
{
try
{
this.context.SaveChanges();
}
catch (DbEntityValidationException exception)
{
var errorMessages = exception.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var exceptionMessage = string.Concat(exception.Message, " The validation errors are: ", fullErrorMessage);
throw new DbEntityValidationException(exceptionMessage, exception.EntityValidationErrors);
}
}
The only two lines that are flagged as not covered are the Select* lines. In this case I can "fix" the coverage stats by restoring it to a single line (the chances of this class being changed again are very small), which will please the people complaining about the total coverage on the solution being below 95%, but it would be interesting to understand more.
Mike
Remco NCrunch Developer
#7403
08 Jun 2015 12:21 UTC
Hi Mike,
Are there any error messages in the collection to enumerate?
If the collection is empty, the enumerator will simply return and won't execute the code.
NCrunch is very physical in its method of capturing line-by-line execution data. It has no knowledge of enumerables, lambda expressions, or any language-specific code constructions. It works simply by framing the lines (which are identified by the PDB file) in instrumentation that will capture if/when the corresponding IL is executed.
Are there any error messages in the collection to enumerate?
If the collection is empty, the enumerator will simply return and won't execute the code.
NCrunch is very physical in its method of capturing line-by-line execution data. It has no knowledge of enumerables, lambda expressions, or any language-specific code constructions. It works simply by framing the lines (which are identified by the PDB file) in instrumentation that will capture if/when the corresponding IL is executed.
I see, I will double check the tests to make sure something is returned. I remember when I wrote this some time ago that creating test ValidationError and ValidationResult objects for testing purposes was very messy. So very possible we skipped some tests on the basis of value vs time invested.
Thanks for the explanations though.
Mike
Thanks for the explanations though.
Mike
Post a reply
Log in to reply.