Fundamentals 16 min read

Writing Unit Tests in Java: JUnit Basics, Test Implementation, and Report Generation

This article explains what unit testing is, why it’s essential, and provides a step‑by‑step guide for writing Java unit tests with JUnit, integrating them into Maven projects, and generating detailed coverage reports using IDE features and JaCoCo.

IT Services Circle
IT Services Circle
IT Services Circle
Writing Unit Tests in Java: JUnit Basics, Test Implementation, and Report Generation

Hello, I am Yupi. Many beginners think that a programmer's job is only to develop new features, but in fact ensuring program stability, improving quality, and maintaining normal operation are also core responsibilities.

Previously I shared the complete development process of enterprise projects, which includes a crucial step called "unit testing". This article discusses how programmers write unit tests.

What is Unit Testing?

Unit testing (Unit Testing, abbreviated UT) is a type of software testing usually written and executed by developers. Compared with other testing types (such as system testing, acceptance testing), it focuses on the smallest testable unit of software.

What does that mean?

Suppose we implement a user registration feature, which may involve many sub‑steps, for example:

Validate whether the user input is legal

Check whether the user is already registered

Add a new user to the database

Each sub‑step can be a small method. To guarantee the correctness of the registration feature, we cannot only test the successful registration case; we should try to cover each sub‑step, testing each small method individually. For instance, we can feed various username/password combinations to verify that the "validate user input" step behaves as expected in both success and failure scenarios.

Similarly, for a complex system composed of many small functions (each represented by a class), we need to write unit tests for each class. Only when every small component works correctly can the whole system run properly.

The core points of unit testing are:

Minimize test scope: unit tests usually target a very small part of the code to keep tests simple and accurate.

Automation: unit tests should be automated so developers can run them anytime to verify code correctness, especially after modifications, without manual checks.

Fast execution: each unit test should run quickly, being lightweight enough for frequent execution.

Independence: each unit test should be independent of other tests and external systems or states, ensuring reliability and repeatability.

Why Do We Need Unit Tests?

By writing and running unit tests, developers can quickly verify that each part of the code works as expected, which is essential for guaranteeing the correct functionality of the system.

In addition, unit testing brings many benefits, such as:

1) Improving code: during the process of writing unit tests, developers revisit business logic and implementation, making it easier to discover code issues and to break complex modules into testable units.

2) Facilitating refactoring: once an automated unit test suite exists, after any code change or refactor, simply re‑run the tests to know whether the modification is correct, greatly improving efficiency and project stability.

3) Documentation: detailed unit tests themselves serve as documentation that describes the expected behavior of the code.

Yupi shares a real example: I once wrote an SQL syntax parsing module that needed to convert more than 10,000 chained‑call statements into standard SQL. Because of many details, after each algorithm improvement I could not guarantee 100% correctness and would manually find a few errors. So I wrote a unit test to automatically verify the parsing result; after each code change I run the test and instantly know whether the conversion is fully correct, greatly improving efficiency.

Therefore, both backend and frontend developers are encouraged to adopt unit testing as a habit, which can effectively improve coding quality.

How to Write Unit Tests?

Using Java as an example, let's learn how to write unit tests.

1. Introduce JUnit

First, we need to add JUnit to the project. Two common ways are shown below.

Maven Project Dependency

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

Spring Boot Project Dependency

If you are using Spring Boot, simply add the spring-boot-starter-test package:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Spring Boot will automatically bring in JUnit Jupiter (JUnit 5), which provides a newer, more flexible way to write and run unit tests. If you already know JUnit 4, you can easily transition to JUnit Jupiter.

2. Write Unit Tests

Writing a unit test usually involves three steps: preparing test data, executing the code under test, and verifying the result.

Generally, each class has a corresponding test class, and each method has a corresponding test method.

Write a JUnit Test

For example, to test a calculator's addition function, the code is as follows:

import org.junit.Test;
import org.junit.Assert;

public class CalculatorTest {

    // Mark the test method with @Test annotation
    @Test
    public void testAdd() {
        // Prepare test data
        long a = 2;
        long b = 3;

        // Execute the code under test
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);

        // Verify the result
        Assert.assertEquals(5, result);
    }
}

The Assert class provides many assertion methods such as assertEquals (equality) and assertNull (null check) to compare the actual output with the expected value.

If the result is correct, the test runner will show a successful output (see image below).

If the result is wrong, the output will clearly show the difference (see image below).

Spring Boot Project Unit Test

For Spring Boot projects we often need to test Mapper and Service beans, which requires the @SpringBootTest annotation to enable dependency injection.

Below is an example testing the user registration feature:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
public class UserServiceTest {

    @Resource
    private UserService userService;

    @Test
    void userRegister() {
        // Prepare data
        String userAccount = "yupi";
        String userPassword = "";
        String checkPassword = "123456";
        // Execute test
        long result = userService.userRegister(userAccount, userPassword, checkPassword);
        // Verify result
        Assertions.assertEquals(-1, result);
        // Prepare another set of data and repeat
        userAccount = "yu";
        result = userService.userRegister(userAccount, userPassword, checkPassword);
        Assertions.assertEquals(-1, result);
    }
}

3. Generate Test Report

If a project contains a large number of unit tests (e.g., 1000), checking a single test case is insufficient. We need a comprehensive test report to view coverage, evaluate effectiveness, and locate issues.

Generate Report with IDEA

In IntelliJ IDEA, select Run xxx with Coverage to execute the test class. The IDE will display a coverage report, as shown in the screenshots below.

The report shows coverage details; for example, the main method may have 0% coverage.

You can also export the report as an HTML document.

Opening the generated index.html in a browser shows a detailed coverage report.

Generate Report with JaCoCo

JaCoCo is a popular Java code‑coverage tool that automatically generates detailed unit‑test reports based on execution results.

First, add the JaCoCo Maven plugin to pom.xml :

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

Then configure the plugin to prepare the agent before tests and generate the report after tests:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
    <configuration>
        <includes>
            <include>com/**/*</include>
        </includes>
    </configuration>
    <executions>
        <execution>
            <id>pre-test</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>post-test</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Run the Maven test command to execute unit tests:

After the tests finish, the JaCoCo report will be generated under the target directory.

Open the generated index.html to view a clear and detailed coverage report, which is especially useful for automating report generation in CI/CD pipelines.

This approach is well‑suited for enterprise environments where automated pipelines generate test reports.

Javasoftware testingMavenunit testingJunittest coverageJaCoCo
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.