Improving Code Coverage with FizzBuzz Unit Testing

Snippet of programming code in IDE
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!