Software Development and QA Tips By QASource Experts

How Do You Differentiate JUnit 4 Tests With JUnit 5?

Written by Timothy Joseph | Oct 11, 2021 4:00:00 PM

JUnit 5 is the latest version of JUnit, which mainly consists of three different subcomponents: JUnit Platform, JUnit Jupiter, and JUnit Vintage. Before learning about these components, a prerequisite about JUnit 5 is that the JUnit 5 runtime requires Java version 8 (or any higher version). However, unit tests can still be compiled with older versions of Java and tested with JUnit 5. Now let me explain the above three subcomponents.


The JUnit platform is the foundation of the testing framework, which helps in the development of the framework. JUnit Jupiter is used to writing tests and JUnit Vintage is used to run older versions of JUnit tests such as JUnit 3 and JUnit 4 based tests.

 

JUnit 5 vs JUnit 4

Let us understand the differences between JUnit 5 and JUnit 4 

  • •In JUnit 4, everything is configured and packaged unlike in JUnit 5. JUnit 5 consists of three components, which are JUnit Platform, JUnit Jupiter, and JUnit Vintage.
  • JUnit 4 requires Java 5 (or higher) and JUnit 5 requires Java 8 (or higher).
  • JUnit 4 does not support third-party integration plug-ins or IDEs. In JUnit 5, the JUnit platform provides support for popular IDEs and translation tools such as Eclipse, Visual Studio and IntelliJ.
  • JUnit confirmsThey are an integral part of the JUnit framework. In JUnit 4, assertions (or assertions) are compiled into the org.junit.Assert package containing all assertions. In JUnit 5, assertion methods are compiled and can be imported from org.junit.jupiter.
  • When assertion error messages are returned, the order of the parameters is different
    • in unit 4, assertEquals public static void (string message, long overdue, real long)
    • in unit 5, General constant void assertion (expected long, real long, string message)

Advantages of Module 5

JUnit 4 has some obvious limitations. The entire framework was contained in a single jar library. The entire library should be imported even when only a specific feature is needed.

In JUnit 5 we get more details and can only import what is necessary. The test runner can only run tests on JUnit 4 at a time (eg SpringJUnit4ClassRunner or Parameterized). JUnit 5 allows multiple brokers to operate simultaneously.

JUnit 4 has never advanced beyond Java 7, it has lost many features of Java 8. JUnit 5 makes good use of Java 8 features. The idea behind JUnit 5 was to completely rewrite JUnit 4 to solve most of these issues.

3. Differences:

Unit 4 is broken down into JUnit 5 modules:

JUnit Platform: This module covers all the extension frameworks that we may be interested in testing.

JUnit Vintage:This module allows backward compatibility with JUnit 4 or even JUnit 3.

  1. Annotations: JUnit 5 comes with important changes within its annotations. The most important one is that we can no longer use @Test annotation for specifying expectations.

    The expected parameter in JUnit 4:

    @Test(expected = Exception.class)
    public void shouldRaiseAnException() throws Exception {
    // ...
    }
    Now, we can use a method assertThrows:

    public void shouldRaiseAnException() throws Exception {
    Assertions.assertThrows(Exception.class, () -> {
    //...
    });
    }

    The timeout attribute in JUnit 4:

    @Test(timeout = 1)

    public void shouldFailBecauseTimeout() throws InterruptedException {

       Thread.sleep(10);

    }

    Now, the assertTimeout method in JUnit 5:

    @Test
    public void shouldFailBecauseTimeout() throws InterruptedException {
    Assertions.assertTimeout(Duration.ofMillis(1), () -> Thread.sleep(10));
    }

    Other annotations that were changed within JUnit 5:

    @Before annotation is renamed to @BeforeEach
    @After annotation is renamed to @AfterEach
    @BeforeClass annotation is renamed to @BeforeAll
    @AfterClass annotation is renamed to @AfterAll
    @Ignore annotation is renamed to @Disabled
  2. Assertions

    We can now write assertion messages in a Lambda in JUnit 5, allowing the lazy evaluation to skip complex message construction until needed:

    @Test
    public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() {
    Assertions.assertTrue(
    2 == 3,
    () -> "Numbers " + 2 + " and " + 3 + " are not equal!");
    }

    We can also group assertions in JUnit 5:

    @Test
    public void shouldAssertAllTheGroup() {
    List<Integer> list = Arrays.asList(1, 2, 4);
    Assertions.assertAll("List is not incremental",
    () -> Assertions.assertEquals(list.get(0).intValue(), 1),
    () -> Assertions.assertEquals(list.get(1).intValue(), 2),
    () -> Assertions.assertEquals(list.get(2).intValue(), 3));
    }
  3. Assumptions

    The new assumptions category is now at org.junit.jupiter.api.Assumptions. JUnit 5 fully supports the existing guessing methods in JUnit 4 and adds a bunch of new methods to allow only certain assertions to be executed in specific scenarios:

    @Test
    public void whenEnvironmentIsWeb_thenUrlsShouldStartWithHttp() {
    assumingThat("WEB".equals(System.getenv("ENV")),
    () -> {
    assertTrue("http".startsWith(address));
    });
    }
  4. Tagging and Filtering

    In JUnit 4 we can compile tests using the Category annotation. With JUnit 5, the Category annotation is replaced by the Tag annotation:

    @Tag("annotations")

    @Tag("junit5")

    @RunWith(JUnitPlatform.class)

    public class AnnotationTestExampleTest {

       /*...*/

    }

    We can include/exclude particular tags using the maven-surefire-plugin:

    <build>

       <plugins>

           <plugin>

               <artifactId>maven-surefire-plugin</artifactId>

               <configuration>

                   <properties>

                       <includeTags>junit5</includeTags>

                   </properties>

               </configuration>

           </plugin>

       </plugins>

    </build>

  5. New Annotations for Running Tests

    RunWith was used to integrate the test context with other frameworks or to change the general execution flow in test cases in Unit 4.

    With JUnit 5 we can now use the ExtendWith annotation to provide similar functionality. As an example, to use Spring functions in Module 4:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(
    {"/app-config.xml", "/test-data-access-config.xml"})
    public class SpringExtensionTest {
    /*...*/
    }
    Now, in JUnit 5 it is a simple extension:
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration(
    { "/app-config.xml", "/test-data-access-config.xml" })
    public class SpringExtensionTest {
    /*...*/
    }
  6. Annotations for The New Test Rules

    In JUnit 4, the @Rule and @ClassRule annotations were used to add special functionality to tests.

    In JUnit 5, we can reproduce the same logic using the @ExtendWith annotation.

    For example, say we have a custom rule in JUnit 4 to write log traces before and after a test:

    public class TraceUnitTestRule implements TestRule {

    @Override
    public Statement apply(Statement base, Description description) {
    return new Statement() {
    @Override
    public void evaluate() throws Throwable {
    // Before and after an evaluation tracing here
    ...
    }
    };
    }
    }

    In addition, we implement it in a test suite:

    @Rule
    public TraceUnitTestRule traceRuleTests = new TraceUnitTestRule();
    In JUnit 5, we can write the same in a much more intuitive manner:

    public class TraceUnitExtension implements AfterEachCallback, BeforeEachCallback {

    @Override
    public void beforeEach(TestExtensionContext context) throws Exception {
    // ...
    }

    @Override
    public void afterEach(TestExtensionContext context) throws Exception {
    // ...
    }
    }

    Using the JUnit 5 interfaces AfterEachCallback and BeforeEachCallback provided in the org.junit.jupiter.api.extension package, we could easily implement this rule in the test suite:

    @RunWith(JUnitPlatform.class)
    @ExtendWith(TraceUnitExtension.class)
    public class RuleExampleTest {



    @Test
    public void whenTracingTests() {
    /*...*/
    }
    }
  7. JUnit 5 Vintage

    JUnit Vintage aids in the migration of JUnit tests by running JUnit 3 or JUnit 4 tests within the JUnit 5 context.

    We can use it by importing the JUnit Vintage Engine:

    <dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>${junit5.vintage.version}</version>
    <scope>test</scope>
    </dependency>
     

If you have any questions about software testing or need to extend your existing QA capabilities, contact QASource for a free quote.