Improving Code Coverage with FizzBuzz Unit Testing
- Published on
Improving Code Coverage with FizzBuzz Unit Testing
Code coverage is an essential metric in software development that measures the proportion of a program's source code that is executed during automated testing. Achieving high code coverage ensures that most parts of the code are tested, leading to fewer bugs and better maintainability. In this blog post, we'll explore how to improve code coverage using unit testing with the classic FizzBuzz problem in Java.
What is FizzBuzz?
FizzBuzz is a popular coding exercise used in technical interviews and programming classes to teach the basics of looping and conditional statements. The problem requires writing a program that prints the numbers from 1 to 100. However, for multiples of three, the program should print "Fizz" instead of the number, and for the multiples of five, it should print "Buzz". For numbers which are multiples of both three and five, the program prints "FizzBuzz".
Setting Up the Project
To begin, let's create a new Java project or open an existing one. For this example, we'll use Maven as the build tool and JUnit as the testing framework. If you don't have Maven installed, you can download and install it from the official website.
Once Maven is installed, you can create a new Maven project using the following command:
mvn archetype:generate -DgroupId=com.example -DartifactId=fizzbuzz -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
This will generate a basic Maven project structure with a pom.xml
file and a src
directory containing the main and test Java folders.
Writing the FizzBuzz Code
Now, let's create a FizzBuzz
class under the com.example
package in the src/main/java
directory. The FizzBuzz
class will contain a method playFizzBuzz()
to generate the FizzBuzz sequence. Here's the initial implementation of the FizzBuzz
class:
package com.example;
public class FizzBuzz {
public String playFizzBuzz(int number) {
if (number % 3 == 0 && number % 5 == 0) {
return "FizzBuzz";
} else if (number % 3 == 0) {
return "Fizz";
} else if (number % 5 == 0) {
return "Buzz";
} else {
return String.valueOf(number);
}
}
}
The playFizzBuzz
method takes an integer number
as input and returns the corresponding FizzBuzz output based on the given rules.
Writing Unit Tests for FizzBuzz
To ensure our FizzBuzz
class is working correctly, we need to write unit tests. Create a new Java class FizzBuzzTest
under the com.example
package in the src/test/java
directory. We'll use JUnit to write the test cases. If you haven't added JUnit to your project, you can include it in the pom.xml
file as a dependency:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
Here's an example of a unit test for the FizzBuzz
class:
package com.example;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class FizzBuzzTest {
private FizzBuzz fizzBuzz;
@Before
public void setUp() {
fizzBuzz = new FizzBuzz();
}
@Test
public void testPlayFizzBuzz_given3_shouldReturnFizz() {
assertEquals("Fizz", fizzBuzz.playFizzBuzz(3));
}
@Test
public void testPlayFizzBuzz_given5_shouldReturnBuzz() {
assertEquals("Buzz", fizzBuzz.playFizzBuzz(5));
}
@Test
public void testPlayFizzBuzz_given15_shouldReturnFizzBuzz() {
assertEquals("FizzBuzz", fizzBuzz.playFizzBuzz(15));
}
@Test
public void testPlayFizzBuzz_given7_shouldReturn7() {
assertEquals("7", fizzBuzz.playFizzBuzz(7));
}
}
In the example above, we've created test methods to cover different scenarios of the playFizzBuzz
method. Each test method uses the assertEquals
method from JUnit to assert the expected output of the playFizzBuzz
method for a given input.
Running the Tests and Improving Code Coverage
After writing the unit tests, we can run them using the Maven command:
mvn test
This command will execute the tests and provide a summary of the test results, including code coverage metrics. To generate a detailed code coverage report, we can use JaCoCo, a widely used Java code coverage library. Add the JaCoCo plugin to the pom.xml
file:
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
After adding the JaCoCo plugin, we can generate the code coverage report by running the command:
mvn verify
The generated report will be available in the target/site/jacoco
directory. Open the index.html
file in a web browser to view the detailed code coverage report.
Analyzing the Code Coverage Report
The code coverage report provides valuable insights into the effectiveness of our unit tests. It shows which parts of the code are covered by the tests and which parts are not. By analyzing the report, we can identify areas with low code coverage and add additional tests to increase coverage.
In the context of our FizzBuzz example, the report may reveal that the playFizzBuzz
method has 100% test coverage, indicating that all code paths are exercised by the tests. However, if we had any additional logic or edge cases in the FizzBuzz class, the report would help identify areas where our tests may be inadequate.
Adding Boundary Tests to Improve Coverage
To further improve code coverage, we can add boundary tests to cover edge cases and additional scenarios. For example, we can add tests to handle zero, negative numbers, or large numbers that may not have been covered in the initial test suite.
@Test
public void testPlayFizzBuzz_given0_shouldReturnFizzBuzz() {
assertEquals("FizzBuzz", fizzBuzz.playFizzBuzz(0));
}
@Test
public void testPlayFizzBuzz_givenNegative3_shouldReturnFizz() {
assertEquals("Fizz", fizzBuzz.playFizzBuzz(-3));
}
@Test
public void testPlayFizzBuzz_given105_shouldReturnFizzBuzz() {
assertEquals("FizzBuzz", fizzBuzz.playFizzBuzz(105));
}
By adding these boundary tests, we ensure that our FizzBuzz implementation handles a wider range of input scenarios, therefore, increasing the overall code coverage.
Wrapping Up
In this blog post, we discussed the importance of code coverage in software development and demonstrated how to improve code coverage using unit testing with the classic FizzBuzz problem in Java. We set up a Maven project, wrote the FizzBuzz code, created unit tests with JUnit, and used JaCoCo for generating a code coverage report. By analyzing the report and adding boundary tests, we were able to improve the overall code coverage of our FizzBuzz implementation.
High code coverage is a good indicator of software quality and can lead to better maintainability and fewer bugs in the long run. Using the techniques outlined in this post, you can apply the principles of code coverage to other parts of your Java projects, ensuring that your code is well-tested and robust.
Remember, code coverage is only one aspect of testing, and it's important to complement it with other testing strategies such as integration testing and end-to-end testing for comprehensive test coverage.
Now it's your turn to apply these principles to your own projects and see the impact on code quality and maintainability. Happy testing!