Visual Studio code coverage tools are only available in the Enterprise edition. This is likely out of reach for most engineers, especially those working on engineering side projects, such as the CodeSloth.blog Code Samples. The Fine Code Coverage Visual Studio 2022 extension, however, is completely free and integrates with the community edition of Visual Studio. You can benefit from code analysis and test coverage reporting for free!
Visual Studio Community Edition
The first step is to install Visual Studio. Head over to the Visual Studio Community download page and install it.
For more details instructions in the context of .Net 6, head over to the Getting Started with .Net 6 blog post, which includes screenshots and specific steps for installing Visual Studio 2022 Community Edition.
Installing the Fine Code Coverage Visual Studio 2022 Extension
Open Visual Studio and select Extensions
-> Manage Extensions
This will launch a dialog box.
- Click the
Online
heading in the left panel - Type
Fine Code Coverage
into the search box - Select
Fine Code Coverage
in the search results list and click Download
Visual Studio will then download the extension
Finally, it will be scheduled for installation after Visual Studio is closed.
Proceed to close Visual Studio. The VSIX Installer will launch.
Click the Modify button to install the extension.
After a short while the installation will complete.
Click the Close
button and re-launch Visual Studio.
Visual Studio Code Coverage with Fine Code Coverage
Once your solution has loaded, re-build it. This will trigger test discovery.
Then, open the Fine Code Coverage window, by selecting View
-> Other Windows
-> Fine Code Coverage
.
Initially the window will be empty.
Trigger a test run, either for a specific project or the entire solution. The respective code will then be analysed and displayed in the Fine Code Coverage window.
After running the tests from the Simplicity
solution from the .Net Test Refactoring Solution (specifically the last blog post), we can see a breakdown of the coverage.
Configuring the Fine Code Coverage Visual Studio 2022 Extension
Disabling Unit Test Project Coverage
In the screenshot above, we can see that the unit testing project itself was included in code coverage. This is unnecessary, as we won’t be analyzing our tests!
Select the Tools
menu -> Options
-> Type fine code
in the filter -> Select Fine Code Coverage
-> set IncludeTestAssembly
to false
. It will be set to true
by default.
Run your tests again and the test project will disappear.
Enabling Microsoft Code Coverage
The default configuration of Fine Code Coverage is to use the ‘old coverage’ tools. However, Microsoft have released a free code coverage solution that this tool has integrated with, so navigate to RunMsCodeCoverage
in the FineCodeCoverage
sections of the Options
dialog and set it to yes
. Click OK
to close the dialog.
Note that in doing so, you will see additional assemblies in the Fine Code Coverage window.
Excluding Noisy Output from Microsoft Code Coverage
The noisy output from above can be excluded by setting the following in the ModulePathsExclude
option.
xunit* Moq*
This setting can be found in the Fine Code Coverage
options, as above.
Configuring Cyclomatic Complexity
Cyclomatic complexity is a metric derived from the number of decision points within a given piece of code. These decision points can be visualised using a control flow graph. Details of this topic will be explored further in another blog post.
All a goal-oriented Code Sloth must understand in using Fine Code Coverage in Visual Studio 2022 is that a large cyclomatic complexity number indicates that your code is making many decisions. Code that makes too many decisions becomes difficult to test and may be susceptible to errors or cognitive complexity that make it difficult to maintain over time.
Final Code Coverage defaults the Microsoft Code Coverage cyclomatic complexity warning to 30.
This is a very large number. Microsoft recommends that a cyclomatic complexity of 10 to 15 is reasonable. Code with a value greater than this will appear in the Risk Hotspots
tab in the Fine Code Coverage window. Start by setting this value to 10 and increase it if required (or refactor to simplify your code).
Using Fine Code Coverage Visual Studio 2022 Reports
Adding Missing Line Coverage
The simplest way to improve your test coverage with the Fine Code Coverage Visual Studio 2022 extension is to identify lines of code that are not covered with tests.
To find lines of code that require test coverage, click on the Line coverage
heading and sort it by least coverage. The reddest progress bar will appear at the top of the list.
Click on the class name to open the source file and then scroll to the red indicator lines on the left-hand side of the text editor.
In the example, we commented out the LaunchARocket_throwsArgumentException_WhenGivenNoMessage
test to get the bar to appear. After uncommenting the test and re-running the Simplicity
test project, we can see the red indicator has turned green, and our progress bar is now 100%.
Adding Missing Branch Coverage
Despite the fact that we have now covered all of the lines of code in this class with tests, the branch coverage progress bar is still partly red. This is because some of the lines of code in this class have conditional logic, and only some of those conditions are tested.
Scrolling a little higher from our previous screenshot, we can see that the RocketLauncher
constructor has null value logic, throwing ArgumentNullExceptions
if true. The happy path of the class’s constructor has been tested thoroughly with our other tests, but no null value handling logic for it has been written yet.
Let’s write three new unit tests to assert these exceptions are thrown”
[Fact] public async void LaunchARocket_throwsArgumentNullException_WhenGivenNullRocketNavigation() { var rocketLaunchingApiMock = new Mock<IRocketLaunchingApi>(); var foodPreparationMock = new Mock<IFoodPreparation>(); Func<RocketLauncher> act = () => new RocketLauncher(null, rocketLaunchingApiMock.Object, foodPreparationMock.Object); act.Should().Throw<ArgumentNullException>(); } [Fact] public async Task LaunchARocket_throwsArgumentNullException_WhenGivenNullRocketLaunchingApi() { var rocketNavigationMock = new Mock<IRocketNavigation>(); var foodPreparationMock = new Mock<IFoodPreparation>(); Func<RocketLauncher> act = () => new RocketLauncher(rocketNavigationMock.Object, null, foodPreparationMock.Object); act.Should().Throw<ArgumentNullException>(); } [Fact] public async Task LaunchARocket_throwsArgumentNullException_WhenGivenNullFoodPreparation() { var rocketNavigationMock = new Mock<IRocketNavigation>(); var rocketLaunchingApiMock = new Mock<IRocketLaunchingApi>(); Func<RocketLauncher> act = () => new RocketLauncher(rocketNavigationMock.Object, rocketLaunchingApiMock.Object, null); act.Should().Throw<ArgumentNullException>(); }
Once again, run the Simplicity
unit tests, and voila, we have complete branch coverage with automated unit tests!
Fixing Risk Hotspots
If you have code that violates the cyclomatic complexity score configured earlier, it will appear in the risk hotspots tab.
Interestingly, Fine Code Coverage seems to report a different cyclomatic complexity (seen as 12 in the Risk Hotspots
tab in the bottom right-hand corner of the screenshot) to Visual Studio’s Code Metric Results (Analyze menu -> Calculate Code Metrics -> For Simplicity project).
Let’s refactor the switch expression into a procedural function:
private (int latitude, int longitude) CalculateCoordinates(int numberOfSloths, string foodForJourney) { if (numberOfSloths < 10) { if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSlightlyToxicLeaves)) { return (123, 456); } else if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSushi)) { return (456, 789); } else { return (000, 111); } } else if (numberOfSloths > 10) { if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSushi)) { return (111, 222); } else if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSlightlyToxicLeaves)) { return (444, 444); } } return (111, 000); }
After re-running the tests and re-analyzing the project:
- Visual Studio Code Metric Results report a cyclomatic complexity of 7
- Fine Code Coverage reports a cyclomatic complexity of 12
Functionally decomposing this logic further will help to reduce this complexity
private (int latitude, int longitude) CalculateCoordinates(int numberOfSloths, string foodForJourney) { if (numberOfSloths < 10) { return CalculateCoordinatesForSmallNumberOfPassengers(foodForJourney); } else if (numberOfSloths > 10) { return CalculateCoordinatesForLargeNumberOfPassengers(foodForJourney); } return (111, 000); } private static (int latitude, int longitude) CalculateCoordinatesForLargeNumberOfPassengers(string foodForJourney) { if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSushi)) { return (111, 222); } else if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSlightlyToxicLeaves)) { return (444, 444); } return (111, 000); } private static (int latitude, int longitude) CalculateCoordinatesForSmallNumberOfPassengers(string foodForJourney) { if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSlightlyToxicLeaves)) { return (123, 456); } else if (string.Equals(foodForJourney, PretendDatabaseClient.FoodSushi)) { return (456, 789); } else { return (000, 111); } }
This change removes the Risk Hotspot
. However, we can still find the Fine Code Coverage cyclomatic complexity number by navigating to bin\Debug\net6.0\fine-code-coverage\coverage-tool-output\<some GUID>\<some name>.xml
Each function has the following cyclomatic complexity
CalculateCoordinates | CalculateCoordinatesForLargeNumberOfPassengers | CalculateCoordinatesForSmallNumberOfPassengers | |
VS Code Metrics | 3 | 3 | 3 |
Fine Code Coverage | 2 | 4 | 4 |
As we can see, making each function responsible for less decisions reduces cyclomatic complexity in the results of both tools.
Sloth Summary
The Fine Code Coverage Visual Studio 2022 extension is great for visualising the unit testing coverage of your code. It provides an easy to interpret progress bars for line/branch coverage in the coverage report, has easy to use navigation that lets you quickly jump into reported classes and indicates test coverage with red/green/yellow indicators to the left of the text editor.
Alongside of these features, being able to configure a treshold for cyclomatic complexity will also help you to write unit tests. The Risk Hotspots tab is a great idea to help you focus your simplification efforts.
It seems that switch expressions are reported differently between Visual Studio’s analyzer and Fine Code Coverage. Depending on the complexity of your switch expression, it may be difficult to understand what branches of logic are missing via the branch coverage report. This however is likely an indicator that simplification of the code is required, given that the code was not simple enough to identify testing gaps in the first place.