JUnit is a simple, open-source framework to write and run repeatable tests in Java. It’s an essential tool for any serious developer who wants to implement unit testing into their development lifecycle. Here’s how you can integrate JUnit 5 into your Maven project.
Prerequisites
Before proceeding, make sure you have:
- Java JDK installed (Java 8 or later)
- Apache Maven installed (3.6.0+)
- Basic understanding of Maven project structure
Step 1: updating the pom.xml file
The first step to adding JUnit to your Maven project is to update your project’s pom.xml file. This XML file describes the software project being built, its dependencies, and the build order.
You can include JUnit in your project by adding it as a dependency. Here is how you do that:
<dependencies>
<!-- JUnit 5 API -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 Engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>
In the example above, I’m using JUnit version 5.9.3. You can find the latest version on Maven Central.
The <scope> tag is set to test, meaning this dependency is not required for normal use of the application, and is only available for the test compilation and execution phases.
Ensuring Maven Surefire compatibility
For Maven to properly execute JUnit 5 tests, you need the Maven Surefire plugin (version 2.22.0 or later):
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
Step 2: writing test cases
Once the pom.xml file is updated, you can begin writing your test cases. By convention, test cases are placed in the src/test/java directory, mirroring the package structure of your main code.
Here is an example of a simple test case:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ExampleTest {
@Test
@DisplayName("Should add two numbers correctly")
public void testAddition() {
int a = 5;
int b = 10;
assertEquals(15, a + b, "5 + 10 should equal 15");
}
}In this test case, we’re using the @Test annotation to denote a test method and @DisplayName to provide a readable test description. The assertEquals() method checks if two values are equal, and the optional third parameter provides a failure message.
JUnit 5 common annotations
JUnit 5 provides several useful annotations for organizing tests:
import org.junit.jupiter.api.*;
public class LifecycleTest {
@BeforeAll
static void setupOnce() {
// Runs once before all tests
}
@BeforeEach
void setupEach() {
// Runs before each test
}
@Test
void testExample() {
// Your test logic
}
@AfterEach
void teardownEach() {
// Runs after each test
}
@AfterAll
static void teardownOnce() {
// Runs once after all tests
}
}
Step 3: running tests
You can run the tests using the Maven command:
mvn test
Maven will automatically discover and run any test cases in the src/test/java directory.
You should see output similar to the following if the tests pass:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running ExampleTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.028 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
The output indicates that one test was run and that there were no failures, errors, or skipped tests.
Running specific tests
You can run specific test classes or methods:
# Run a specific test class
mvn test -Dtest=ExampleTest
# Run a specific test method
mvn test -Dtest=ExampleTest#testAddition
Best practices
When integrating JUnit into your Maven projects, consider these best practices:
- Match package structure: Keep your test packages mirrored to your source packages for easy navigation
- Naming conventions: Name test classes with the
Testsuffix (e.g.,CalculatorTest) - Descriptive test names: Use
@DisplayNamefor human-readable test descriptions - Arrange-Act-Assert: Structure tests with clear setup, execution, and verification phases
- One assertion per test: Keep tests focused on a single behavior (when practical)
- Use parameterized tests: For testing multiple inputs, leverage
@ParameterizedTest
Troubleshooting
Tests not running? Ensure you have both junit-jupiter-api and junit-jupiter-engine dependencies, and verify your Maven Surefire plugin version is 2.22.0 or later.
Import errors? Run mvn clean install to refresh dependencies.
Conclusion
Integrating JUnit 5 into your Maven project is straightforward and brings significant benefits to code quality and maintainability. With proper test coverage, you can refactor with confidence, catch bugs early, and maintain a robust codebase.
Further reading
- JUnit 5 User Guide - Official comprehensive documentation
- Baeldung JUnit 5 Guide - Practical tutorials and examples
- Maven Surefire Plugin - Plugin documentation