Test coverage

When we discuss tests, it's quite common to hear the term test coverage. This is a measure used to check how much code is being executed by the suite tests, and it's helpful for determining what alternative paths of code are not tested and are hence prone to bugs.

Let's suppose that you are writing a method that has an if statement. In that case, your code has two alternative paths to follow; so, if you want to achieve 100% coverage, you will need to write tests to validate all of the alternative routes that your code can follow.

There are many useful libraries available for measuring the coverage that code has. In this chapter, we are going to introduce one of the most popular libraries in the Java world; the library is called JaCoCo (http://www.eclemma.org/jacoco/).

In order to make JaCoCo a part of our application, we need to include it as a plugin, using our preferred build tool.

The following is the required configuration to include JaCoCo using Gradle:

apply plugin: "jacoco"
jacoco
{
toolVersion = "VERSION"
}

The following is the required configuration to include JaCoCo using Maven:

<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>VERSION</version>
</plugin>

Once JaCoCo has been included as a part of the project, we will have new tasks that can be used to measure the coverage of our code. Let's generate a coverage report by executing the following Gradle task:

$ ./gradlew test jacocoTestReport

The coverage report that is generated will be available in HTML format, as shown in the following screenshot:

JaCoCo report

Although it is true that we want to achieve high coverage for our code, we need to be careful with what kinds of tests we are writing because, with this approach in mind, we may be tempted to write useless tests, just to achieve 100% coverage.

To fully understand what I'm talking about here, let's review the report generated by JaCoCo for one of the classes in the domain package:

Test coverage report for a domain class

The report shows that there are no tests at all for some methods. Some of these methods are standard for any Java object, and the others are only getters and setters (accessors), which do not need to be tested. Writing getters and setters often leads to building anemic domain models, and, most of the time, this is only used to make the code compatible with frameworks relying on the Java Beans convention. For this reason, there is no need to write tests to cover getters and setters.

I have seen people writing tests for these methods only to achieve 100% coverage, but that is a useless and impractical procedure that should be avoided, as it doesn't add any value to the quality of the code or the written tests. 

Now, let's review the report for one of the classes that has some logic worth testing:

JaCoCo coverage report for a service class

Surprisingly, this class has 100% coverage. Let's review the associated tests for this class, as follows:

@RunWith(MockitoJUnitRunner.class)
public class BankingUserDetailServiceTest
{
@Mock
CustomerRepository customerRepository;
@InjectMocks
BankingUsersDetailService bankingUsersDetailService;
@Test(expected = UsernameNotFoundException.class)
public void whenTheUserIsNotFoundAnExceptionIsExpected()
throws Exception
{
String username = "foo";
Mockito.when(customerRepository.findByUsername(username))
.thenReturn(Optional.empty());
bankingUsersDetailService.loadUserByUsername(username);
}
@Test
public void theUserDetailsContainsTheInformationFromTheFoundCustomer
() throws Exception
{
String username = "foo";
String password = "bar";
Customer customer =
new Customer(username, password, NotificationType.EMAIL);
Mockito.when(customerRepository.findByUsername(username))
.thenReturn(Optional.of(customer));
UserDetails userDetails = bankingUsersDetailService
.loadUserByUsername(username);
Assert.assertEquals(userDetails.getUsername(), username);
Assert.assertEquals(userDetails.getPassword(), password);
Assert.assertEquals(userDetails.getAuthorities()

.iterator().next().getAuthority(), "ROLE_CUSTOMER");
}
}

We cannot always achieve 100% coverage, as we did in this example. However, a good measure tends to be 80%. You must think of the previously mentioned percentage not as a rule, but a recommendation; if you verify that your tests are exercising all of the logic needed, sometimes a value less than 80% will be fine. 

You need to be smart, using the generated report to figure out what logic needs to be tested and then work on it, rather than feel frustrated by the results.

One of the good things about using this kind of tool is that you can integrate it as part of your continuous integration server, to generate reports that are visible all of the time. In this way, the reports can be used to continually check whether the coverage is growing or going down and take action. We will discuss this topic in more detail in Chapter 11, DevOps and Release Management.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.89.18