27.1 C
New York
Friday, June 6, 2025

How to test your Java applications with JUnit 5



Listing 5. Logging the invocations of JUnit 5 lifecycle methods (LifecycleDemoTest.java)


package com.javaworld.geekcap.lifecycle;

import org.junit.jupiter.api.*;

public class LifecycleDemoTest {

    @BeforeAll
    static void beforeAll() {
        System.out.println("Connect to the database");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("Load the schema");
    }

    @AfterEach
    void afterEach() {
        System.out.println("Drop the schema");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("Disconnect from the database");
    }

    @Test
    void testOne() {
        System.out.println("Test One");
    }

    @Test
    void testTwo() {
        System.out.println("Test Two");
    }
}

The output from running this test prints the following:


Connect to the database
Load the schema
Test One
Drop the schema
Load the schema
Test Two
Drop the schema
Disconnect from the database

As you can see from this output, the beforeAll method is called first and may do something like connect to a database or create a large data structure into memory. Next, the beforeEach method prepares the data for each test; for example, by populating a test database with an expected set of data. The first test then runs, followed by the afterEach method. This process (beforeEach—> test—>afterEach) continues until all the tests have completed. Finally, the afterAll method cleans up the test environment, possibly by disconnecting from a database.

Before wrapping up this initial introduction to testing with JUnit 5, I’ll show you how to use tags to selectively run different kinds of test cases. Tags are used to identify and filter specific tests that you want to run in various scenarios. For example, you might tag one test class or method as an integration test and another as development code. The names and uses of the tags are all up to you.

We’ll create three new test classes and tag two of them as development and one as integration, presumably to differentiate between tests you want to run when building for different environments. Listings 6, 7, and 8 show these three simple tests.

Listing 6. JUnit 5 tags, test 1 (TestOne.java)


package com.javaworld.geekcap.tags;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("Development")
class TestOne {
    @Test
    void testOne() {
        System.out.println("Test 1");
    }
}

Listing 7. JUnit 5 tags, test 2 (TestTwo.java)


package com.javaworld.geekcap.tags;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("Development")
class TestTwo {
    @Test
    void testTwo() {
        System.out.println("Test 2");
    }
}

Listing 8. JUnit 5 tags, test 3 (TestThree.java)


package com.javaworld.geekcap.tags;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("Integration")
class TestThree {
    @Test
    void testThree() {
        System.out.println("Test 3");
    }
}

Tags are implemented through annotations, and you can annotate either an entire test class or individual methods in a test class; furthermore, a class or a method can have multiple tags. In this example, TestOne and TestTwo are annotated with the “Development” tag, and TestThree is annotated with the “Integration” tag. We can filter test runs in different ways based on tags. The simplest of these is to specify a test in your Maven command line; for example, the following only executes tests tagged as “Development”:


mvn clean test -Dgroups="Development"

The groups property allows you to specify a comma-separated list of tag names for the tests that you want JUnit 5 to run. Executing this yields the following output:


[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.javaworld.geekcap.tags.TestOne
Test 1
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.029 s - in com.javaworld.geekcap.tags.TestOne
[INFO] Running com.javaworld.geekcap.tags.TestTwo
Test 2
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 s - in com.javaworld.geekcap.tags.TestTwo

Likewise, we could execute just the integration tests as follows:


mvn clean test -Dgroups="Integration"

Or, we could execute both development and integration tests:


mvn clean test -Dgroups="Development, Integration"

In addition to the groups property, JUnit 5 allows you to use an excludedGroups property to execute all tests that do not have the specified tag. For example, in a development environment, we do not want to execute the integration tests, so we could execute the following:


mvn clean test -DexcludedGroups="Integration"

This is helpful because a large application can have literally thousands of tests. If you wanted to create this environmental differentiation and add some new production tests, you would not want to have to go back and add a “Development” tag to the other 10,000 tests.

Finally, you can add these same groups and excludedGroups fields to the surefire plugin in your Maven POM file. You can also control these fields using Maven profiles. I encourage you to review the JUnit 5 user guide to learn more about tags.

Conclusion

This article introduced some of the highlights of working with JUnit 5. I showed you how to configure a Maven project to use JUnit 5 and how to write tests using the @Test and @ParameterizedTest annotations. I then introduced the JUnit 5 lifecycle annotations, followed by a look at the use and benefits of filter tags.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles