How do I use tags to group JUnit tests?

In JUnit 5, you can use the @Tag annotation to group tests into categories such as:

  • fast
  • slow
  • unit
  • integration
  • database
  • api
  • smoke

Tags are useful when you want to run only certain groups of tests, for example only fast unit tests during development, or only integration tests in a CI pipeline.


1. Basic Example

Use @Tag on a test method:

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

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

class CalculatorTest {

    @Test
    @Tag("fast")
    void shouldAddTwoNumbers() {
        Calculator calculator = new Calculator();

        int result = calculator.add(2, 3);

        assertEquals(5, result);
    }

    @Test
    @Tag("slow")
    void shouldCalculateLargeDataSet() {
        Calculator calculator = new Calculator();

        int result = calculator.processLargeDataSet();

        assertEquals(1000, result);
    }
}

In this example:

  • shouldAddTwoNumbers() belongs to the fast group.
  • shouldCalculateLargeDataSet() belongs to the slow group.

2. Tag an Entire Test Class

You can place @Tag on a class to apply the tag to all tests inside it.

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

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

@Tag("unit")
class StringUtilsTest {

    @Test
    void shouldReturnTrueForBlankString() {
        assertTrue(StringUtils.isBlank(""));
    }

    @Test
    void shouldReturnTrueForWhitespaceString() {
        assertTrue(StringUtils.isBlank("   "));
    }
}

All tests in StringUtilsTest are now tagged as unit.


3. Use Multiple Tags

A test can have more than one tag.

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

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

class UserServiceTest {

    @Test
    @Tag("unit")
    @Tag("fast")
    void shouldCreateUser() {
        UserService userService = new UserService();

        User user = userService.createUser("[email protected]");

        assertNotNull(user);
    }

    @Test
    @Tag("integration")
    @Tag("database")
    void shouldSaveUserToDatabase() {
        UserRepository userRepository = new UserRepository();

        User user = userRepository.save(new User("[email protected]"));

        assertNotNull(user.getId());
    }
}

This allows you to run tests by different categories.

For example:

  • Run all unit tests.
  • Run all fast tests.
  • Run all integration tests.
  • Exclude all database tests.

4. Run Tagged Tests with Maven

If you are using Maven Surefire, you can run tests with a specific tag like this:

mvn test -Dgroups=unit

To run multiple tags:

mvn test -Dgroups=unit,fast

To exclude a tag:

mvn test -DexcludedGroups=slow

Example:

mvn test -Dgroups=integration -DexcludedGroups=slow

This runs tests tagged with integration, but excludes tests tagged with slow.


5. Configure Tags in pom.xml

You can also configure Maven to include or exclude tags permanently.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.5.2</version>
            <configuration>
                <groups>unit</groups>
                <excludedGroups>slow</excludedGroups>
            </configuration>
        </plugin>
    </plugins>
</build>

This configuration runs tests tagged unit and excludes tests tagged slow.


6. Run Tagged Tests with Gradle

If you are using Gradle, configure the test task:

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

Then run:

gradle test

You can also create separate test tasks:

tasks.register('integrationTest', Test) {
    useJUnitPlatform {
        includeTags 'integration'
    }
}

Run it with:

gradle integrationTest

7. Tag Integration Tests

A common use case is separating unit tests from integration tests.

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

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

@Tag("integration")
class UserRepositoryTest {

    @Test
    void shouldConnectToDatabase() {
        boolean connected = true;

        assertTrue(connected);
    }
}

Then you can run only integration tests:

mvn test -Dgroups=integration

Or exclude them during regular builds:

mvn test -DexcludedGroups=integration

8. Tag Nested Tests

Tags also work with nested test classes.

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

class OrderServiceTest {

    @Nested
    @Tag("validation")
    class ValidationTests {

        @Test
        void shouldRejectInvalidOrder() {
            // validation test
        }

        @Test
        void shouldAcceptValidOrder() {
            // validation test
        }
    }

    @Nested
    @Tag("pricing")
    class PricingTests {

        @Test
        void shouldCalculateDiscount() {
            // pricing test
        }

        @Test
        void shouldApplyTax() {
            // pricing test
        }
    }
}

Here:

  • All tests in ValidationTests are tagged validation.
  • All tests in PricingTests are tagged pricing.

9. Tag Naming Rules

JUnit tag names must follow a few rules:

  • A tag must not be blank.
  • A tag must not contain whitespace.
  • A tag must not contain ISO control characters.
  • A tag must not contain reserved characters:
    • ,
    • (
    • )
    • &
    • |
    • !

Good examples:

@Tag("unit")
@Tag("integration")
@Tag("fast")
@Tag("database")
@Tag("smoke")

Avoid:

@Tag("unit test")
@Tag("fast,unit")
@Tag("integration&database")

10. Best Practices

Use tags for broad execution groups, not for every small detail.

Good tag categories:

  • unit
  • integration
  • slow
  • fast
  • database
  • api
  • smoke

Avoid over-tagging tests with too many labels.

For example, this is usually enough:

@Test
@Tag("integration")
@Tag("database")
void shouldSaveCustomer() {
}

But this is probably too much:

@Test
@Tag("integration")
@Tag("database")
@Tag("repository")
@Tag("customer")
@Tag("save")
@Tag("positive")
void shouldSaveCustomer() {
}

Summary

Use JUnit 5 @Tag to group and filter tests.

@Test
@Tag("fast")
void shouldRunQuickly() {
}

You can apply tags to:

  • Individual test methods
  • Entire test classes
  • Nested test classes

Then run selected groups using Maven or Gradle:

mvn test -Dgroups=fast
test {
    useJUnitPlatform {
        includeTags 'fast'
    }
}

Tags are especially useful for separating unit tests, integration tests, slow tests, and database-dependent tests.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.