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.
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.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.