To write your first JUnit 5 test, add JUnit Jupiter to a Java project, create a method annotated with @Test, and use an assertion to check the result. Then add setup and cleanup where needed, use parameterized tests for multiple inputs, and run the suite through your IDE or build tool. This tutorial walks through those five steps with Maven and Gradle examples.
1. Add JUnit Jupiter to a Maven or Gradle project
JUnit 5 is an umbrella for three parts: the JUnit Platform, which launches testing frameworks; JUnit Jupiter, the modern API and programming model used here; and JUnit Vintage, which supports older JUnit tests on the Platform. For a new test, use Jupiter and make sure your build tool can run tests on the Platform. The JUnit 5 User Guide provides starter guidance for Maven and Gradle and describes IDE support.
Maven
Add the JUnit Jupiter test dependency to your project and use a recent Maven Surefire or Failsafe version with JUnit Platform support. Surefire runs tests in the test phase; Failsafe is commonly used for integration-test phases. The JUnit guide recommends recent plugin versions to reduce launcher-version interoperability problems. Consult its Maven setup instructions for the current configuration suited to your project.
Gradle
Add JUnit Jupiter dependencies to the test configuration, then enable Platform execution in the Gradle test task:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →#1 Best Overall
test {
useJUnitPlatform()
}
This setting tells Gradle to execute tests through the JUnit Platform. See Gradle’s Java testing documentation for the current testing configuration details.
2. Write one test and one assertion
A test checks a particular behavior. The annotation marks the method as a test, and the assertion compares the expected result with the actual result:
Rank #2
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class CalculatorTest {
@Test
void addition() {
assertEquals(2, 1 + 1);
}
}
@Test and the Jupiter assertions are in the org.junit.jupiter.api package. In a real project, call the production code rather than putting the calculation directly in the test. For example, replace 1 + 1 with a call to a calculator method, then compare its result with the expected value.
The assertion form is assertEquals(expected, actual). A descriptive method name such as addition tells you what behavior the test covers. Keep the first test small and deterministic: when it passes, you should be able to see why the result is correct.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Rank #3
3. Add setup, cleanup, and test isolation
Use @BeforeEach to prepare what a test needs and @AfterEach to release resources after it finishes. These methods run around each test, making them useful for repeatable setup and cleanup.
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class ExampleTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@Test
void addsNumbers() {
// Exercise calculator and assert the result.
}
@AfterEach
void tearDown() {
// Release resources if this test used any.
}
}
JUnit Jupiter creates a new test-class instance for each test method by default, its per-method lifecycle. This helps prevent one test’s instance fields from accidentally carrying state into another test. Keep setup focused on the data each test needs; if shared mutable state is necessary, reset it deliberately rather than relying on test order.
Rank #4
@TestInstance(Lifecycle.PER_CLASS) opts into one instance per test class. Choose it only when a shared class-level lifecycle is intentional; it changes the default instance behavior and requires care with mutable state. The lifecycle and annotations are documented in the JUnit 5 User Guide.
4. Use a parameterized test for multiple inputs
If one behavior should work for several inputs, a parameterized test avoids duplicating nearly identical test methods. A parameterized test runs its method multiple times with arguments supplied by an argument source; it requires at least one source. For example:
Best Value
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class PalindromeTest {
@ParameterizedTest
@ValueSource(strings = {"racecar", "radar"})
void acceptsPalindromes(String candidate) {
assertTrue(isPalindrome(candidate));
}
}
Here, @ValueSource supplies each string to the method. Each invocation is reported separately, so a failure can be traced to the input that caused it. Parameterized tests and their available argument sources are covered in the JUnit 5 User Guide.
5. Run the suite and confirm the test is working
You can run tests from an IDE that supports JUnit or through the project build. Gradle’s useJUnitPlatform() enables discovery through the Platform; Maven runs tests through a Platform-capable Surefire or Failsafe plugin. Use the command appropriate to your project root:
- Gradle: run
./gradlew teston macOS or Linux, orgradlew teston Windows, when the project includes the Gradle wrapper. - Maven: run
./mvnw teston macOS or Linux, ormvnw teston Windows, when the project includes the Maven wrapper. For integration tests, use the phase configured for Failsafe in that project.
For the first run, verify the test is discovered and a passing assertion is reported. Then temporarily change the expected value—for example, change assertEquals(2, 1 + 1) to assertEquals(3, 1 + 1)—and run the test again. The failure should show the expected and actual values. Restore the correct assertion afterward. This checks that the test is executing, not merely compiling.
Quick Recap
Choosing the right JUnit approach
| Choice | Best fit | Key consideration |
|---|---|---|
| Maven | Projects built with Maven | Use a recent Surefire or Failsafe plugin with JUnit Platform support. |
| Gradle | Projects built with Gradle | Configure the test task with useJUnitPlatform(). |
Ordinary @Test |
A distinct scenario or behavior | Direct and readable for an individual case. |
@ParameterizedTest |
The same behavior checked against multiple inputs | Use an argument source; each invocation is reported separately. |
| Per-method lifecycle | Most tests, especially those with mutable instance state | The default creates a new test instance for each method. |
| Per-class lifecycle | A deliberate shared test-class instance | Opt in with @TestInstance(Lifecycle.PER_CLASS) and manage mutable state carefully. |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Recommended Free Tools

