Leveraging the percentage of code covered by Unit Tests can be a critical metric when monitoring the maintainability of your solution. However, it can also be a futile effort that will result in substantial efforts expended for limited results. Additionally, having seen specific development teams working under mandates to obtain a particular percentage to meet a bonus or other goal, I decided to dive into methods to control reported values regarding code coverage properly. The data behind a metric is only as good as the inputs going into it, and often a raw "% Covered" number can be very misleading to those unaware.
Why Bother?
Before I dive into specific changes, I believe it is essential to quantify the reason that we might want to try and control the metrics and modify the overall % of coverage reported. We should not look at this as gaming the system, but working to limit our scope the most critical elements of our application. Ultimately we are working to identify how protected we are from inadvertent and breaking changes in our code and accuracy in reporting can be the key to success.
Considerations
In a perfect world, we would all write easily testable code, and to that point, Microsoft and others would provide easily testable APIs. However, the reality is far from that utopia. I recently blogged about a side-effort that created from this reality in my Unit Testing .NET Core Statics post where I discussed some of the most common problem areas when writing code that looks innocent enough. Areas that might be considered for exclusion include:
- Startup.cs (As much as I'd love to test this, practical sense says this might not be possible. See some StackOverflow discussions of how this could get ugly quickly.)
- Automatically Generated Items (EF Migrations, etc.)
- Third-Party Integrations Dependent Upon Extension Methods
These are just some examples, and your particular code/needs might add or remove things from this list. The key is to be sure to balance risk vs. reward as well as complexity in any exclusion. Exclusions shouldn't be used as a way to hide unnecessarily ugly code, but as a method to better identify the code that is critical or important.
Adjusting Reporting Patterns
When it comes to methods to hide items from the Code Coverage reports, there are typically two methods that I will see employed to adjust results:
Moving Assemblies
For certain concepts, it is possible to move items to a separate assembly, and once done, you can then ignore that new assembly in your reporting results. A great example of this would be with your Entity Framework Migrations. It is possible to Use a Separate Migration Assembly to hold all of these in one location. This type of solution is fantastic for those elements that truly can be moved into another bucket; for EF Migrations, I strongly recommend this approach as it creates a well-defined structure of exclusion.
Exclusion of Individual Items
Other aspects of your code might require a more surgical approach. The ExcludeFromCodeCoverage attribute will be your pathway to success. This attribute can be applied to your code at an Assembly, Class, Constructor, Method, or Property level. Anything with this applied will be excluded from the calculation of Code Coverage %, simply add [ExcludeFromCodeCoverage] to the item you wish to exclude.
Conclusion
In an ideal world, we could use the data 100% as-is; however, the reality is slightly different. You will need to use these tips with an abundance of caution to avoid sweeping stuff under the rug, but by using a calculated approach, you can paint a much better experience of your project. Code review and similar processes will be critical to prevent abuse.