Getting Started with JUnit 5: Setup and First Test in Java

Illustration for Getting Started with JUnit 5: Setup and First Test in Java
By Last updated:

For decades, JUnit has been the gold standard of testing in Java. With JUnit 5, developers gained a modern, extensible, and powerful framework that makes unit testing more expressive and maintainable. Whether you’re new to testing or migrating from JUnit 4, JUnit 5 is the foundation you’ll build on for reliable software in today’s CI/CD-driven world.

In this tutorial, we’ll cover how to set up JUnit 5 in your project, write your first test, explore core annotations, and integrate it into modern development workflows.


Why JUnit 5 Matters

  • Bug Prevention: Catch issues early before production.
  • Maintainability: Safer refactoring with automated safety nets.
  • CI/CD Integration: Seamless execution in Jenkins, GitHub Actions, and GitLab CI.
  • Modern Features: Parameterized tests, nested tests, extensions, and dependency injection.

Setting Up JUnit 5

JUnit 5 consists of three main modules:

  • JUnit Platform – Test engine and launcher.
  • JUnit Jupiter – New programming model and annotations.
  • JUnit Vintage – Backward compatibility with JUnit 4.

Maven Setup

<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0-M7</version>
        </plugin>
    </plugins>
</build>

Gradle Setup

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

test {
    useJUnitPlatform()
}

Writing Your First JUnit 5 Test

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

class Calculator {
    int add(int a, int b) {
        return a + b;
    }
}

class CalculatorTest {
    @Test
    void testAddition() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result, "2 + 3 should equal 5");
    }
}

Explanation

  • @Test – Marks a method as a test.
  • assertEquals – Checks expected vs. actual results.
  • Test passes if values match, fails otherwise.

Core Annotations in JUnit 5

  • @Test – Marks a method as a test case.
  • @BeforeEach / @AfterEach – Runs before/after each test method.
  • @BeforeAll / @AfterAll – Runs once before/after all tests (must be static).
  • @Disabled – Skips a test.
  • @ParameterizedTest – Runs a test with multiple inputs.
  • @Nested – Allows grouping of related tests.

Example: Lifecycle Annotations

import org.junit.jupiter.api.*;

class LifecycleTest {
    @BeforeAll
    static void initAll() { System.out.println("Before all tests"); }

    @BeforeEach
    void init() { System.out.println("Before each test"); }

    @Test
    void testExample() { System.out.println("Running test"); }

    @AfterEach
    void tearDown() { System.out.println("After each test"); }

    @AfterAll
    static void tearDownAll() { System.out.println("After all tests"); }
}

Parameterized Tests

JUnit 5 supports parameterized tests, reducing code duplication.

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertTrue;

class ParameterizedExampleTest {
    @ParameterizedTest
    @ValueSource(strings = {"racecar", "radar", "level"})
    void testPalindrome(String word) {
        assertTrue(isPalindrome(word));
    }

    boolean isPalindrome(String word) {
        return word.equals(new StringBuilder(word).reverse().toString());
    }
}

Advanced Features

  • Dynamic Tests: Generate tests programmatically at runtime.
  • Dependency Injection: Inject test info and temp directories into tests.
  • Extensions API: Customize test execution (e.g., SpringExtension for Spring Boot).
  • Coverage with JaCoCo: Measure and enforce test coverage.

Real-World Scenarios

  1. Spring Boot: Use @SpringBootTest with JUnit 5 to load application context.
  2. Mockito: Combine with JUnit 5 for mocking dependencies.
  3. Testcontainers: Run integration tests against real Dockerized services.
  4. CI/CD Pipelines: Run JUnit tests automatically on commits with GitHub Actions.

Best Practices

  • Keep tests small and isolated.
  • Use descriptive test names (shouldAddTwoNumbers).
  • Avoid unnecessary dependencies in unit tests.
  • Run tests frequently with Maven/Gradle.
  • Include tests in CI/CD pipelines.

Version Tracker

  • JUnit 4 → JUnit 5 Transition: New annotations (@BeforeAll, @AfterAll), Jupiter engine, and modular design.
  • Mockito Enhancements: Support for static/final mocking.
  • Testcontainers Growth: Expanded modules for databases, message brokers, and cloud services.

Conclusion & Key Takeaways

JUnit 5 is the modern foundation for Java testing. By setting it up with Maven/Gradle and writing your first test, you’ve taken the first step toward building maintainable, production-ready test suites.

Key Takeaways:

  • JUnit 5 offers powerful annotations and modern features.
  • It integrates seamlessly with build tools and CI/CD pipelines.
  • Combined with Mockito and Testcontainers, it supports unit, integration, and E2E testing.

FAQ

1. How is JUnit 5 different from JUnit 4?
JUnit 5 introduces the Jupiter API, modular architecture, and better annotations.

2. Do I need Maven or Gradle for JUnit 5?
No, but build tools make setup and automation easier.

3. Can JUnit 5 run JUnit 4 tests?
Yes, using the JUnit Vintage engine.

4. How do I measure coverage with JUnit 5?
Use JaCoCo with Maven/Gradle to generate coverage reports.

5. How do I mock dependencies in JUnit 5?
Use Mockito with @ExtendWith(MockitoExtension.class).

6. Can I run JUnit 5 tests in IntelliJ/Eclipse?
Yes, modern IDEs provide built-in support.

7. What’s a parameterized test?
A test that runs multiple times with different input values.

8. Does JUnit 5 support dependency injection?
Yes, via TestInfo, TestReporter, and extensions like Spring.

9. Can I use JUnit 5 in legacy projects?
Yes, alongside JUnit 4 tests with the Vintage engine.

10. Should I migrate from JUnit 4 to 5?
Yes, for long-term maintainability and modern features.