How do you include or exclude TestNG groups from Gradle, and what does that control?
answer
- includeGroups / excludeGroups
- @Test(groups = ...) string tags
- exclude wins over include
- parameterize via -P property
- smoke-on-push, full-nightly
basics
~10 sInside useTestNG { ... } call includeGroups("smoke") and excludeGroups("slow"). TestNG groups are named tags on @Test methods, and these options select which tagged tests run.
solid answer
~40 sTestNG groups are arbitrary string tags declared on tests via `@Test(groups = ["smoke"])`. Gradle exposes them through `TestNGOptions` inside the `useTestNG { }` closure: `includeGroups("smoke", "regression")` runs only tests tagged with those groups, and `excludeGroups("slow", "flaky")` removes tests so tagged. Both can be combined — exclusion wins over inclusion for a test in both sets. This is the Gradle-side equivalent of TestNG's `<groups>` element in suite XML, and it lets CI pipelines run, say, only the `smoke` group on every push and the full suite nightly without changing test code. A common pattern is to read the group from a Gradle property (`-PtestGroups=smoke`) so the same build script serves multiple pipelines.
code
kotlin · 6 linestasks.named<Test>("test") {
useTestNG {
includeGroups("smoke")
excludeGroups("slow", "flaky")
}
}go deeper
Know that includeGroups/excludeGroups exist and that groups are @Test labels.
Explain exclude-over-include precedence and parameterizing groups via a Gradle property for CI tiers.
Discuss layering with suite XML, avoiding split ownership of selection, and using groups as the fast-feedback lever.
Frame group strategy as a CI architecture decision: tiered pipelines (smoke/nightly/full) driven by one parameterized build.
## What a TestNG group is A *group* is just a named label you attach to a test method or class: ```java @Test(groups = {"smoke", "login"}) public void loginWorks() { ... } ``` Groups are TestNG's primary mechanism for slicing a suite — far more flexible than JUnit 4's categories. A single method can belong to many groups, and groups can depend on other groups. ## Selecting groups from Gradle Gradle's `TestNGOptions` (the receiver of the `useTestNG { }` closure) mirrors TestNG's selection semantics: ```kotlin tasks.named<Test>("test") { useTestNG { includeGroups("smoke", "regression") excludeGroups("slow") } } ``` - **includeGroups** — only tests in at least one listed group run. If you list none, all tests are eligible. - **excludeGroups** — tests in any listed group are dropped, even if they also match an include. Exclusion takes precedence, which matters when a test carries both an included and an excluded group. ## Driving selection from the command line Hard-coding groups in the build script is rigid. The idiomatic approach is to parameterize: ```kotlin tasks.named<Test>("test") { useTestNG { val groups = (project.findProperty("testGroups") as String?)?.split(",") groups?.forEach { includeGroups(it.trim()) } } } ``` Then `./gradlew test -PtestGroups=smoke` runs only the smoke group, while a bare `./gradlew test` runs everything. CI can thus reuse one build definition across smoke/nightly/full pipelines. ## Relationship to suite XML Whatever you set here is layered with any TestNG suite XML you also supply. If a `testng.xml` already constrains `<groups>`, Gradle's options and the XML both apply; keep ownership of group selection in one place to avoid confusion. ## Why it matters for build performance Group selection is the cheapest lever for keeping fast feedback: run a tiny smoke group on every commit (seconds) and reserve the slow integration group for scheduled builds, all without recompiling or duplicating tests.
- A test is tagged with both 'smoke' and 'slow'; you includeGroups('smoke') and excludeGroups('slow'). Does it run?No. Exclusion takes precedence over inclusion, so the test is filtered out.
- How would you make the active group selectable per CI pipeline without editing the build script?Read a Gradle project property (e.g. -PtestGroups=smoke) inside the useTestNG closure and feed it to includeGroups, defaulting to all tests when unset.
saying these in an interview costs you the question
- Confusing TestNG groups with JUnit 5 tags/@Tag — different APIs.
- Assuming includeGroups and excludeGroups are mutually exclusive (they compose).
- Saying include wins when both match — exclusion wins.