skip to content

Filter Block & Tags

Declarative include and exclude patterns in the filter block, plus JUnit tag filtering to separate slow tests from fast ones. Interviewers ask how you would split a suite across CI stages.

on this pageshow

questions

5

What does the `filter {}` block inside a `Test` task do, and how do `includeTestsMatching` and `excludeTestsMatching` work?

level: juniorimportance: must knowfreq 55%

answer

  1. filter {} on Test task
  2. includeTestsMatching / excludeTestsMatching
  3. FQN + * wildcard
  4. OR-ed inclusions
  5. failOnNoMatchingTests default true

basics

~10 s

The filter {} block on a Test task selects which tests run by pattern. includeTestsMatching('*Foo*') keeps only matching tests; excludeTestsMatching drops matching ones. Patterns match fully-qualified class/method names with * wildcards.

solid answer

~30 s

Every `Test` task (like `test`) exposes a persistent `filter {}` block in the build script. `includeTestsMatching(pattern)` whitelists tests whose fully-qualified name matches; `excludeTestsMatching(pattern)` blacklists them. Patterns use `*` as a wildcard and dot-separate package, class and method (`com.acme.OrderTest.shouldPay`, `*IntegrationTest`, `*.slow*`). Multiple include patterns are OR-ed together. Unlike the transient `--tests` command-line option, `filter {}` is declared in the build and applies on every run, making it the right tool for permanent partitioning (e.g. excluding slow tests from the default `test` task). By default `failOnNoMatchingTests` is true, so a pattern matching nothing fails the build — useful to catch typos.

code

kotlin · 7 lines
kotlin
tasks.test {
    filter {
        includeTestsMatching("com.acme.*Test")
        excludeTestsMatching("*SlowTest")
        isFailOnNoMatchingTests = true
    }
}

go deeper

for a junior

Know that filter {} selects tests by name pattern with *, and the include/exclude method names.

for a middle

Explain FQN matching, OR-ing of includes, exclude-wins precedence, and failOnNoMatchingTests.

for a senior

Contrast persistent build-script filtering with transient --tests; know when to prefer tags over name patterns.

for a principal

Frame name-filters as brittle coupling to naming conventions; advocate tag-based categorization as the governance-friendly partitioning strategy across many modules.

## What the filter block is Gradle's JVM test execution is driven by `Test` tasks (the built-in `test` task is one; you can register more). Each `Test` task carries a `TestFilter` accessed through the `filter {}` configuration block. The filter narrows the set of discovered tests **before** they execute, independent of the test engine (JUnit 4, JUnit Platform, TestNG). ## The two methods - `includeTestsMatching(String pattern)` — adds an **inclusion** pattern. If any inclusion pattern is present, only tests matching at least one of them run (inclusions are OR-ed). - `excludeTestsMatching(String pattern)` — adds an **exclusion** pattern. Any test matching it is dropped, even if it matched an inclusion. ## Pattern syntax Patterns are matched against the **fully-qualified name** in the form `package.Class.method`. The only wildcard is `*` (matches any sequence of characters, including dots). Examples: - `com.acme.OrderTest` — every method of that class - `com.acme.OrderTest.shouldChargeCard` — a single method - `*IntegrationTest` — every class whose simple/qualified name ends in `IntegrationTest` - `*.ui.*` — anything in a `ui` sub-package There is no `?` single-char wildcard and no regex; it is glob-like only. ## Persistent vs. transient `filter {}` is **declared in the build script**, so it applies on every invocation of that task — the natural place to permanently shape a task (e.g. an `integrationTest` task that only ever runs `*IT`). The command-line `--tests` option is a **one-off override** for a single run and lives in the sibling topic. ## failOnNoMatchingTests ```kotlin tasks.test { filter { includeTestsMatching("*FastTest") isFailOnNoMatchingTests = true // default } } ``` With the default `true`, a filter that selects zero tests fails the build, catching renamed-away or mistyped patterns. Set it to `false` when an empty result is legitimate (e.g. a task that may have nothing to run in some modules). ## How it relates to tags The `filter {}` block matches on **names**. To partition by semantic category (slow/fast/db) you typically use JUnit Platform `@Tag` plus `useJUnitPlatform { includeTags(...) }` — a complementary mechanism. Name-filtering and tag-filtering can be combined on the same task.

  • If both an include and an exclude pattern match the same test, what happens?
    Exclusion wins — the test is dropped. Excludes are applied after includes.
  • What wildcard characters does the pattern support?
    Only `*`. It is glob-like, not a regex; there is no `?` and no character classes.

saying these in an interview costs you the question

  • Claiming the patterns are regular expressions (they are glob-like with only `*`).
  • Saying `filter {}` only works with JUnit 5 — it is engine-agnostic and pre-dates the platform.
  • Confusing `filter {}` with the `--tests` CLI flag as if they were the same persistent mechanism.

context

open as a page

How do you partition tests by `@Tag` using `useJUnitPlatform { includeTags / excludeTags }`?

level: middleimportance: must knowfreq 50%

basics

~10 s

Annotate tests with JUnit 5 @Tag("slow"). In the Test task call useJUnitPlatform { includeTags("slow") } to run only tagged tests, or excludeTags("slow") to skip them. The platform discovers tests by tag.

open as a page

When should you use name-based `filter {}` versus tag-based `useJUnitPlatform { includeTags }`, and how do they interact on the same task?

level: middleimportance: should knowfreq 38%

basics

~20 s

Use name filters for structural/package selection (*IntegrationTest); use tags for semantic categories that survive renames (slow, db). On one task both apply: a test must pass the name filter AND the tag filter to run.

open as a page

Design a multi-task test partitioning scheme so unit tests run fast by default and slow/integration tests run on demand, using filter blocks and tags.

level: seniorimportance: should knowfreq 32%

basics

~10 s

Tag slow tests @Tag("slow"). Make the default test task excludeTags("slow"). Register a separate integrationTest Test task that includeTags("slow"), give it its own classpath, and wire check to depend on it in CI.

open as a page

What are the common pitfalls of test selection — pattern semantics, the JUnit 4 vs Platform divide, and silently dropped tests?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Pitfalls: assuming patterns are regex (they're glob * only); using @Tag/includeTags while still on useJUnit() (JUnit 4 ignores them); and name-based excludes silently missing tests after renames. Also failOnNoMatchingTests defaulting on can break filtered runs.

open as a page