Tagging and Filtering Tests in JUnit: Fast vs Slow Test Execution

Illustration for Tagging and Filtering Tests in JUnit: Fast vs Slow Test Execution
By Last updated:

Not all tests are created equal. Some run in milliseconds, while others involve databases, microservices, or Docker containers and take several minutes. Running all tests on every commit can slow down developers and CI/CD pipelines. The solution? Tagging and filtering tests in JUnit 5.

By categorizing tests as fast or slow (or by feature, module, or environment), developers can selectively run only the tests they need. This strategy ensures rapid feedback during development while maintaining full coverage in nightly builds or release pipelines.


Why Tagging and Filtering Tests Matters

  • Productivity: Run fast unit tests locally without waiting for heavy integration tests.
  • CI/CD Efficiency: Separate smoke tests from long-running suites.
  • Maintainability: Keep test suites manageable as applications scale.
  • Flexibility: Choose test subsets based on context (e.g., regression, database, performance).

Think of tagging like labels in Gmail — you organize tests by category and run them as needed.


Introducing @Tag in JUnit 5

JUnit 5 introduces the @Tag annotation to classify tests. You can apply tags at both the class level and the method level.

Example: Tagging Unit and Integration Tests

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

class UserServiceTest {

    @Test
    @Tag("fast")
    void shouldCreateUserQuickly() {
        System.out.println("Fast unit test running...");
    }

    @Test
    @Tag("slow")
    void shouldIntegrateWithDatabase() {
        System.out.println("Slow integration test running...");
    }
}

Filtering Tests with Maven

You can filter tagged tests in Maven using the Surefire or Failsafe plugin.

Run only fast tests

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M7</version>
    <configuration>
        <groups>fast</groups>
    </configuration>
</plugin>

Exclude slow tests

<configuration>
    <excludedGroups>slow</excludedGroups>
</configuration>

Run from CLI:

mvn test -Dgroups=fast
mvn test -DexcludedGroups=slow

Filtering Tests with Gradle

Gradle makes test filtering straightforward with useJUnitPlatform.

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

Run from CLI:

./gradlew test --tests * --info --include-tag fast

Organizing Tests: Fast vs Slow

  • Fast Tests: Pure unit tests with mocks (Mockito), no external dependencies.
  • Slow Tests: Integration tests with DBs, Kafka, or external services (Testcontainers).

Example: Fast Unit Test with Mockito

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
import static org.mockito.Mockito.*;

class OrderServiceTest {

    @Test
    @Tag("fast")
    void shouldSaveOrderWithMock() {
        OrderRepository repo = mock(OrderRepository.class);
        OrderService service = new OrderService(repo);

        service.placeOrder(new Order("Book"));
        verify(repo).save(any(Order.class));
    }
}

Example: Slow Integration Test with Testcontainers

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
import org.testcontainers.containers.PostgreSQLContainer;

class DatabaseIntegrationTest {

    @Test
    @Tag("slow")
    void shouldRunWithPostgresContainer() {
        try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
            postgres.start();
            System.out.println("JDBC URL: " + postgres.getJdbcUrl());
            assert(postgres.isRunning());
        }
    }
}

Sample output:

Creating container for image: postgres:15
Container started in 11.7s
JDBC URL: jdbc:postgresql://localhost:32782/test

Real-World Use Cases

  1. CI/CD Pipelines: Run fast tests on every commit, slow integration tests nightly.
  2. Microservices: Tag contract tests separately from database integration tests.
  3. Legacy Codebases: Introduce tags gradually to organize chaotic test suites.
  4. Performance Testing: Tag load tests (@Tag("performance")) to run only on demand.

Best Practices

  • Always tag slow tests explicitly (@Tag("slow")).
  • Keep unit tests fast and independent.
  • Run a smoke suite (fast tests) before merging PRs.
  • Execute all tests (fast + slow) in staging and before releases.
  • Document tagging strategy in the project’s README or contributor guide.

Version Tracker

  • JUnit 4 → JUnit 5: JUnit 4 used @Category, JUnit 5 replaced it with @Tag.
  • Mockito Updates: New inline mocking improves speed for unit tests.
  • Testcontainers Growth: More DB, Kafka, and LocalStack modules encourage tagging for integration tests.

Conclusion & Key Takeaways

Tagging and filtering in JUnit 5 allow developers to balance speed and coverage. By running fast tests frequently and slow tests strategically, teams achieve both productivity and reliability.

Key Takeaways:

  • Use @Tag("fast") and @Tag("slow") to classify tests.
  • Filter tests in Maven/Gradle for flexible execution.
  • Combine Mockito for fast unit tests and Testcontainers for slow integration tests.
  • Apply tagging consistently to maximize CI/CD efficiency.

FAQ

1. What replaced @Category in JUnit 5?
@Tag replaced @Category for tagging tests.

2. Can I apply multiple tags to one test?
Yes, you can apply multiple @Tag annotations to a single test method.

3. How do I run only integration tests?
Use @Tag("integration") and configure Maven/Gradle to include only that tag.

4. Are slow tests bad?
Not at all — they are essential, but should be run less frequently (e.g., nightly).

5. Can I combine tags with assumptions?
Yes, you can use both for flexible test execution.

6. Do IDEs support filtering by tag?
Yes, IntelliJ and Eclipse allow running tests filtered by tags.

7. Can Testcontainers tests be tagged as fast?
Generally no — container startup time makes them slow.

8. What is the best tagging strategy?
At minimum, use fast for unit tests and slow for integration tests.

9. Can tags help with flaky tests?
Yes, you can isolate flaky tests with a special tag for debugging.

10. Should I always tag tests?
Yes, tagging improves organization and CI/CD efficiency, especially in large projects.