Skip to content

Common updates from JUnit 4 to JUnit 5

hulkoba edited this page Oct 11, 2024 · 35 revisions

Most of the stuff can be found here

https://www.baeldung.com/junit-5-migration

Basics

Make sure the imports are added as static. To all org.junit. imports you have to add jupiter.api like:

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

@Test(expected=...)

@Test(expected = UnsupportedOperationException.class) public void ...() { convertToEntity(); }

turns to

import static org.junit.jupiter.api.Assertions.assertThrows;

@Test
public void testConvertToEntityAttribute() {
    assertThrows(UnsupportedOperationException.class, () -> this.converter.convertToEntityAttribute(null));
}

Assertions

Update the import to org.junit.jupiter.api.Assertions

Note for any assertions with error messages, the message is moved as the second argument.

For example:

// JUnit 4
assertTrue("No messages returned", messages != null && messages.size() > 0);

// JUnit 5
assertTrue(messages != null && messages.size() > 0, "No messages returned");

Setup and Teardown

import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.BeforeAll;

Replace @Before with @BeforeEach

Replace @BeforeClass with @BeforeAll

Replace @After with @AfterEach

Replace @AfterClass with @AfterAll

Category

Remove @Category because JUnit 5 doesn't support categories like JUnit 4. Instead, it uses tags (@Tag), which you can adapt based on your requirements.

Import @Tag like so: import org.junit.jupiter.api.Tag;

Then use @Tag as follows:

For example: @Category(AsyncLoggers.class) is replaced with @Tag("AsyncLoggers")

RunWith

MockitoJUnitRunner

Add imports:

import org.mockito.junit.jupiter.MockitoExtension;

import org.junit.jupiter.api.extension.ExtendWith;

Replace @RunWith(MockitoJUnitRunner.class) with @ExtendWith(MockitoExtension.class)

Parameterized

  • Replace @RunWith(Parameterized.class) with @ParameterizedTest. Also remove @Test as @ParameterizedTest will cover that.
  • Instead of @Parameterized.Parameters, use @MethodSource to supply the test parameters
  • Remove the constructor, as JUnit 5 passes parameters directly to the test methods
  • The data() method now returns a Stream<>, which JUnit 5 uses for parameterized tests

Example

// JUnit 5
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;

public static Stream<String> data() {
    return Stream.of("logger-config/LoggerConfig/", "logger-config/AsyncLoggerConfig/");
}

@ParameterizedTest(name = "{0}")
@MethodSource("data")

// JUnit 4
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import com.google.common.collect.ImmutableList;
import java.util.List;

@RunWith(Parameterized.class)
@Parameterized.Parameters(name = "{0}")
public static List<String> data() throws IOException {
    return ImmutableList.of("logger-config/LoggerConfig/", "logger-config/AsyncLoggerConfig/");
}

@Test

AssertThat

Since JUnit 5 does not directly provide assertThat, we switch to using org.hamcrest.MatcherAssert.assertThat from the hamcrest library.

Replace org.junit.Assert.assertThat with org.hamcrest.MatcherAssert.assertThat

Rules

ClassRule

@ClassRule is deprecated in Junit5 and, unfortunately, the translation is not straight forward. It depends on the implementation. At the moment we have successfully migrated one of is usages in this PR

I'll explain this example:

  • Now the class has a new interface @LoggerContextSource, to which we pass the value of the config file (value = "log4j2-jdbc-dbcp2.xml") and the timeout (the timeout value is from LoggerContextRule.createShutdownTimeoutLoggerContextRule)
  • In the test function, we no longer need to get the appender as it is provided as a parameter of the function. @Named is to specify which appender’s name you want to fetch. Before we did it like final Appender appender = LCR.getAppender("databaseAppender");, but now (I think) this function is inside the interface @LoggerContextSource, so we only need to specify the appender as a parameter in the test function public void test(@Named("databaseAppender") final Appender appender) {
import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
import org.apache.logging.log4j.core.test.junit.Named;

@LoggerContextSource(value = "log4j2-jdbc-dbcp2.xml", timeout = 10)

Rule

ThreadContextStackRule

Junit4: `` public class ContextStackJsonAttributeConverterTest { private ContextStackJsonAttributeConverter converter;

@Rule
public final ThreadContextStackRule threadContextRule = new ThreadContextStackRule();
junit5

@UsingThreadContextStack public class ContextStackJsonAttributeConverterTest {...}

Please update this section when they request changes for the PR we'll do in their side, to reflect the end result they are looking for.

Clone this wiki locally