Archive for February, 2010

Unit testing coverage with PartCover and ReportGenerator

Saturday, February 13th, 2010

In an ideal software development world we would test all of the code that we write. So it would be nice if we would be able to tell how much of the code is covered by our own unittests and the cool thing is; there are free tools around that help us discover our code coverage.

Here is a small endevour in that world. I’ve started with a small labs-project with some class that has almost the simplest logic you can think of. Here’s the code.

public class BusinessClass
{

public int SomeCalculation()
{
int a = 1;
a += 1;
return a;
}

public int AddOneToNumbersAboveThree(int myNumber)
{
if (myNumber <= 3) { return myNumber; } return myNumber += 1; } }

Now I'm deliberately going to write a unittest that only covers the AddOneToNumbersAboveThree method, just to see what PartCover + ReportGenerator come up with if I do not test one of the methods.

Here's the unit test class:

[TestFixture]
public class TestClass
{
[Test]
public void BusinessClass_AddOne_Success()
{
BusinessClass businessClass = new BusinessClass();
int result = businessClass.AddOneToNumbersAboveThree(5);
Assert.AreEqual(6,result,"No succes for method");
}
}

Now that my labs-project is ready, it is time to setup PartCover and ReportGenerator. What PartCover is actually doing, is running all of your unittests by calling NUnit, and parsing the results of that into its own xml-report.
Than after that, this PartCoverReport can be fed to ReportGenerator to generate beautifull HTML pages to show your code coverage.

Wat I am used to do is create batch files to run all the stuff. Here's the batch file that I eventually came up with:

set partcoverfolder="Dependencies\PartCover"
REM set the NUnit arguments for PartCover
set targetworkdir=PartCover.UnitTest\bin\Debug
set targetnunitconsole="C:\Program Files\NUnit 2.4.6\bin\nunit-console.exe"
set targetnunitargs=PartCover.UnitTest.dll

REM set the assemblies to include exclude for PartCover and run
set includeassemblyname=--include *PartCoverClass*
set excludeassemblyname=--exclude *UnitTest*
%partcoverfolder%\PartCover.exe --target %targetnunitconsole% --target-work-dir %targetworkdir% --target-args %targetnunitargs% --output %targetworkdir%\PartCoverReport.xml %includeassemblyname% %excludeassemblyname%

REM now generate the report
set reportgeneratorfolder=Dependencies\ReportGenerator\bin
%reportgeneratorfolder%\ReportGenerator.exe %targetworkdir%\PartCoverReport.xml %targetworkdir%\PartCoverHtml\

This batchfile might need some explanation. What happens is that PartCover.Exe is executing the NUnit testframework. We have to pass all the paths where NUnit has to store its TestResults.xml to PartCover. So,
1) PartCover runs and starts up NUnit.exe.
2) Nunit runs all the tests and places the TestResults.xml
3) PartCover takes up the TestResults.xml that NUnit created and creates a PartCoverReport.xml

So here is the PartCover Report of this simple run:












This is not really human-readable at all. Luckily the people from palmmedia.de created ReportGenerator which parses the above file into nice HTML, and than all your code coverage finally becomes clear:

ReportGenerator

And there you have it. The unittest that I created is covering part of the method, but I have not (yet) written a unit test that covers the else-part of the method. Of course, this is just a very simple example with a very simple method and this will all be much more interesting for a very large project that has some more complicated methods. At least there is a way to see what parts of your code are (not) covered by the tests you write against it.

I have also sent a message to palmmedia.de to tell that I was kind of surprised that the Report did not say that the other method SomeCalculation() was 'not covered'.

Daniel Palme of palmmedia.de was very kind to quickly sent me a response to my question

[..] This is an issue I already know. The problem is that the report generated by PartCover does not contain enough information. So it is not possible to determine whether a line has not been visited or is
not visitable when the method has not been called [via Nunit] at all.
[..]

Unfortunately it is not possible to fix this as Daniel explains in his blogpost so lets see what the branches of PartCover like SharpCover can come up with...