@DisplayName @Tag

In JUnit 5, default test labels often fall short. Reports show class names and method names, which may be precise but rarely helpful. A name like UserServiceTest and registerShouldFailWhenUserNotExists tells you something about structure, but not much about intent.

Good test names should turn a test into a small piece of documentation. They should answer questions like what behavior is expected, under which condition, and why it matters. That is where @DisplayName and @Tag become useful.
@DisplayName provides a way to add a custom display name to a test class, method, parameterized test, or nested class. The chosen text is what readers see in reports, IDEs, and test output. It can be plain English, include spaces, or use symbols. It does not replace the method name or change how the test runs.

@DisplayName("User service tests")
class UserServiceTest {

    @Test
    @DisplayName("Registration should fail when the username already exists")
    void registerShouldFailWhenUserExists() {
        // test logic
    }

    @Test
    @DisplayName("A valid request should create a user")
    void registerShouldCreateUser() {
        // test logic
    }
}

For larger suites, @DisplayName becomes especially useful. It can clarify parameterized cases and nested test groups. Instead of showing only technical identifiers, it can express expected behavior.

When manual labels are too repetitive, @DisplayNameGeneration can automate naming. The ReplaceUnderscores strategy turns snake_case into readable text. A method named adds_two_positive_numbers would appear as adds two positive numbers.

Organizing Tests with @Tag

@Tag is JUnit 5's mechanism for grouping tests. A test can have multiple tags, and tags can be applied at class level. This makes it possible to classify tests by speed, scope, or business area.

class PaymentServiceTest {

    @Test
    @Tag("fast")
    @Tag("unit")
    void shouldCalculateDiscount() {
        // quick unit test
    }

    @Test
    @Tag("slow")
    @Tag("integration")
    void shouldConnectToPaymentGateway() {
        // slow integration test
    }
}

The @Tags annotation can combine multiple tags, though repeating @Tag is equivalent. Once tests are tagged, build tools can filter them.

Maven uses the Surefire plugin configuration:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <groups>fast | unit</groups>
        <excludedGroups>slow</excludedGroups>
    </configuration>
</plugin>

Gradle uses the JUnit platform configuration:

test {
    useJUnitPlatform {
        includeTags 'fast', 'unit'
        excludeTags 'slow'
    }
}

Tag expressions can use logical operators, such as fast & !flaky or integration | e2e. This gives teams flexible execution rules.

IDEs such as IntelliJ IDEA also support tag filtering. Developers can run a single tag, exclude another, or execute all tests in a group. This is useful for quick feedback.

Best Practices

  • Use descriptive display names: focus on behavior, not implementation details.
  • Keep tag names stable: prefer a shared convention over ad hoc labels.
  • Avoid excessive tagging: a small set of categories is easier to maintain.
  • Remember display names do not affect execution: they are for reporting and readability.
  • Wire tags into CI: run fast tests early and broader tests later.

Conclusion

@DisplayName and @Tag solve different but complementary problems. One improves how test names communicate meaning. The other improves how tests can be grouped and selected. Used together, they make JUnit 5 test suites easier to read, organize, and run.