Close

JUnit 5 - Programmatic Test Filtering

[Last Updated: Dec 15, 2025]

For advanced scenarios where command-line or IDE filtering is insufficient, JUnit 5 provides a programmatic API through the JUnit Platform Launcher. This allows you to create custom test runners, implement complex filtering logic, and integrate testing into custom build systems or tools.

When to Use Programmatic Filtering

Programmatic filtering is useful for:

  • Custom test runners with dynamic test selection
  • Integration with proprietary build systems
  • Complex filtering logic not supported by standard tools
  • Generating custom test reports and analytics
  • Creating specialized testing tools or plugins

Introduction to Luncher Api

The Launcher API is the programmatic entry point to the JUnit Platform.
All runners (Maven, Gradle, IDEs) use it and eventually do the following steps:

(1) Add following extra maven dependency

pom.xml

<dependency>
   <groupId>org.junit.platform</groupId>
   <artifactId>junit-platform-launcher</artifactId>
   <version>1.10.0</version>
</dependency>

(2) Selecting Tests (Discovery) and filtering

   LauncherDiscoveryRequest request = 
        LauncherDiscoveryRequestBuilder.request()
                                       .selectors(
            DiscoverySelectors.selectClass(...),
            DiscoverySelectors.selectPackage(.....))
                                       .filters(
            TagFilter.includeTags("<filter expression>"),
            TagFilter.excludeTags("<filter expression>"))
                                       .build();

(3) Creating the Launcher

    Launcher launcher = LauncherFactory.create();

(4) Executing Tests

    launcher.execute(request);

Example

Test classes

package com.logicbig.example;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Tag("calc-test")
class MyCalcTest {

    @Test
    void primeNumberTest() {
        assertEquals(2, 1 + 1);
    }

    @Test
    void sumTest() {
        assertTrue(true);
    }
}
package com.logicbig.example;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Tag("integration")
class MyIntegrationTest {

    @Test
    void dbTransactionTest() {
        assertTrue(true);
    }

    @Test
    @Tag("staging")
    void stagingTest() {
        assertEquals(2, 1 + 1);
    }

    @Test
    @Tag("calc-test")
    void testFactorial(){
        assertTrue(true);
    }
}

Using Luncher Api

package com.logicbig.example;

import org.junit.platform.engine.TestExecutionResult;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;

import org.junit.platform.launcher.TagFilter;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;

public class TagFilteringLauncher {

    public static void main(String[] args) {

        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
                                                                          .selectors(
                        DiscoverySelectors.selectClass(MyIntegrationTest.class),
                        DiscoverySelectors.selectClass(MyCalcTest.class)
                )
                                                                          .filters(
                        TagFilter.includeTags("integration | calc-test"),
                        TagFilter.excludeTags("staging")
                )
                                                                          .build();

        Launcher launcher = LauncherFactory.create();

        // Create summary listener
        SummaryGeneratingListener listener = new SummaryGeneratingListener(){
           //to print what test passed
            @Override
            public void executionFinished(TestIdentifier testIdentifier,
                                          TestExecutionResult testExecutionResult) {
                if (testIdentifier.isTest()
                        && testExecutionResult.getStatus() == TestExecutionResult.Status.SUCCESSFUL) {

                    System.out.println("PASSED: " + testIdentifier.getDisplayName());
                }
            }
        };

        // Execute with listener
        launcher.execute(request, listener);

        // Print summary
        TestExecutionSummary summary = listener.getSummary();

        System.out.println("=================================");
        System.out.println("Test run finished");
        System.out.println("Tests found   : " + summary.getTestsFoundCount());

        System.out.println("Tests started : " + summary.getTestsStartedCount());
        System.out.println("Tests passed  : " + summary.getTestsSucceededCount());
        System.out.println("Tests failed  : " + summary.getTestsFailedCount());
        System.out.println("Tests skipped : " + summary.getTestsSkippedCount());
        System.out.println("=================================");

    }
}

Output

PASSED: testFactorial()
PASSED: dbTransactionTest()
PASSED: sumTest()
PASSED: primeNumberTest()
=================================
Test run finished
Tests found : 4
Tests started : 4
Tests passed : 0
Tests failed : 0
Tests skipped : 0
=================================

Example Project

Dependencies and Technologies Used:

  • junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
     Version Compatibility: 5.2.0 - 6.0.1Version List
    ×

    Version compatibilities of junit-jupiter-engine with this example:

    • 5.2.0
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.4.0
    • 5.4.1
    • 5.4.2
    • 5.5.0
    • 5.5.1
    • 5.5.2
    • 5.6.0
    • 5.6.1
    • 5.6.2
    • 5.6.3
    • 5.7.0
    • 5.7.1
    • 5.7.2
    • 5.8.0
    • 5.8.1
    • 5.8.2
    • 5.9.0
    • 5.9.1
    • 5.9.2
    • 5.9.3
    • 5.10.0
    • 5.10.1
    • 5.10.2
    • 5.10.3
    • 5.10.4
    • 5.10.5
    • 5.11.0
    • 5.11.1
    • 5.11.2
    • 5.11.3
    • 5.11.4
    • 5.12.0
    • 5.12.1
    • 5.12.2
    • 5.13.0
    • 5.13.1
    • 5.13.2
    • 5.13.3
    • 5.13.4
    • 5.14.0
    • 5.14.1
    • 6.0.0
    • 6.0.1

    Versions in green have been tested.

  • junit-platform-launcher 6.0.1 (Module "junit-platform-launcher" of JUnit)
  • JDK 25
  • Maven 3.9.11

JUnit 5 - Programmatic Test Filtering Select All Download
  • junit-5-programmatic-test-filtering
    • src
      • test
        • java
          • com
            • logicbig
              • example
                • TagFilteringLauncher.java

    See Also