Step‑by‑Step Migration from JUnit 4 to JUnit 5 in a Spring Boot Project

This tutorial guides you through creating a basic Spring Boot calculator app, adding JUnit 4 tests, then fully migrating the project to JUnit 5 by updating dependencies, rewriting test annotations, and leveraging new JUnit 5 features such as @ExtendWith and @DisplayName.

ITPUB
ITPUB
ITPUB
Step‑by‑Step Migration from JUnit 4 to JUnit 5 in a Spring Boot Project

In this hands‑on tutorial you will build a simple Spring Boot 2.0.3 calculator application, write unit tests with JUnit 4, and then perform an end‑to‑end migration to JUnit 5.

Setup a JUnit 4 Spring Boot project

Create a Maven project named JUnit4 in Eclipse, add the spring-boot-starter-web and spring-boot-starter-test dependencies, and include JUnit 4 as a test scope dependency. The provided pom.xml snippet shows the required configuration.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>springbootJUnit4</groupId>
  <artifactId>unit4</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
  </parent>
  <dependencies>
    <dependency>
      <groupId>JUnit</groupId>
      <artifactId>JUnit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
  </dependencies>
</project>

Implement four simple beans: Adder, Subtractor, Multiplier, and Calculator. The Calculator bean autowires the other three and performs basic validation.

package springbootJUnit4.unit4;

import org.springframework.stereotype.Component;

@Component
public class Adder {
    public Integer add(Integer a, Integer b) {
        return a + b;
    }
}

@Component
public class Subtractor {
    public Integer subtract(Integer a, Integer b) {
        return a - b;
    }
}

@Component
public class Multiplier {
    public Integer multiply(Integer a, Integer b) {
        return a * b;
    }
}

@Component
public class Calculator {
    @Autowired Adder adder;
    @Autowired Subtractor subtractor;
    @Autowired Multiplier multiplier;

    public Integer add(Integer a, Integer b) {
        if (a < 0 || b < 0) throw new IllegalArgumentException("Invalid input positive integers only");
        return adder.add(a, b);
    }
    public Integer subtract(Integer a, Integer b) {
        if (a < b) throw new IllegalArgumentException("first argument cannot be less than second");
        return subtractor.subtract(a, b);
    }
    public Integer multiply(Integer a, Integer b) {
        if (a == 0 || b == 0) throw new IllegalArgumentException("Input cannot be zero");
        return multiplier.multiply(a, b);
    }
}

Write JUnit 4 tests

Create CalculatorTest using @RunWith(SpringJUnit4ClassRunner.class) and @SpringBootTest. Mock the dependent beans with @MockBean and verify behavior with Mockito.

package springbootjunit4.unit4;

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class CalculatorTest {
    @MockBean Adder adder;
    @MockBean Subtractor subtractor;
    @Autowired Calculator calculator;

    @Test
    public void testAddition() {
        Mockito.when(adder.add(Mockito.anyInt(), Mockito.anyInt())).thenReturn(22);
        Integer result = calculator.add(10, 12);
        assertEquals(Integer.valueOf(22), result);
    }
    // additional tests for negative inputs and subtraction omitted for brevity
}

Create a JUnit 5 project

Clone the JUnit 4 project, rename it (e.g., sb-junit5), and update the pom.xml to Spring Boot 2.2.3. Add the JUnit 5 dependencies and exclude the junit-vintage-engine to prevent JUnit 4 execution.

<project ...>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.3.RELEASE</version>
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</project>

Convert test code to JUnit 5

Replace @RunWith with @ExtendWith(SpringExtension.class), import JUnit 5 packages, and use @DisplayName for readable test names. The new test class looks like:

package springbootJUnit4.unit4;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class CalculatorTest {
    @MockBean Adder adder;
    @MockBean Subtractor subtractor;
    @Autowired Calculator calculator;

    @Test
    @DisplayName("Test positive integer addition")
    public void testAddition() {
        Mockito.when(adder.add(Mockito.anyInt(), Mockito.anyInt())).thenReturn(22);
        Integer result = calculator.add(10, 12);
        assertEquals(Integer.valueOf(22), result);
    }

    @Test
    @DisplayName("Test addition failure for one negative argument")
    public void testAdditionOneNegativeNumber() {
        assertThrows(IllegalArgumentException.class, () -> calculator.add(-10, 12));
    }

    @Test
    @DisplayName("Test addition failure for both arguments negative")
    public void testAdditionBothNegativeNumber() {
        assertThrows(IllegalArgumentException.class, () -> calculator.add(-10, -12));
    }

    @Test
    @DisplayName("Test positive case for subtraction")
    public void testSubtraction() {
        Mockito.when(subtractor.subtract(Mockito.anyInt(), Mockito.anyInt())).thenReturn(1);
        Integer result = calculator.subtract(11, 10);
        assertEquals(Integer.valueOf(1), result);
    }
}

Conclusion

The exercise demonstrates a complete migration path from JUnit 4 to JUnit 5 within a Spring Boot application, covering dependency changes, annotation updates, and the use of new JUnit 5 features such as @ExtendWith and @DisplayName. It also highlights the importance of excluding the vintage engine to fully embrace JUnit 5. The provided GitHub repository (https://github.com/developer-help/junit5-migration) contains the full source code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

migrationMavenunit testingspring-bootMockitoJUnit 5JUnit 4
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

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.