Home .Net Visual Studio Code Coverage with Fine Code Coverage Visual Studio 2022 Extension

Visual Studio Code Coverage with Fine Code Coverage Visual Studio 2022 Extension

by Trent
0 comments
Fine Code Coverage Visual Studio Extension Featured Image

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.

  1. Click the Online heading in the left panel
  2. Type Fine Code Coverage into the search box
  3. Select Fine Code Coverage in the search results list and click Download
fine code coverage visual studio 2022 extension download

Visual Studio will then download the extension

fine code coverage visual studio 2022 extension downloading

Finally, it will be scheduled for installation after Visual Studio is closed.

visual studio code coverage extension pending restart

Proceed to close Visual Studio. The VSIX Installer will launch.

code coverage visual studio vsix installer dialog

Click the Modify button to install the extension.

visual studio code coverage extension installation

After a short while the installation will complete.

fine code coverage visual studio 2022 extension installation 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.

fine code coverage visual studio 2022 window

Initially the window will be empty.

visual studio code coverage blank window

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.

fine code coverage default output

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.

visual studio code coverage exclude test assembly

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.

visual studio code coverage ms code coverage option

Note that in doing so, you will see additional assemblies in the Fine Code Coverage window.

visual studio code coverage additional assemblies

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.

fine code coverage visual studio 2022 cyclomatic complexity configuration

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.

fine code coverage line coverage

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 Simplicitytest project, we can see the red indicator has turned green, and our progress bar is now 100%.

fine code coverage line coverage fixed

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.

fine code coverage missing branch coverage

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!

fine code coverage branch coverage completed

Fixing Risk Hotspots

If you have code that violates the cyclomatic complexity score configured earlier, it will appear in the risk hotspots tab.

conflicting cyclomatic complexity scores

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

CalculateCoordinatesCalculateCoordinatesForLargeNumberOfPassengersCalculateCoordinatesForSmallNumberOfPassengers
VS Code Metrics333
Fine Code Coverage244

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.

You may also like