1.2.6 Test Suites

test_suite gives an existing set of tests one stable Bazel label. That is useful when a team wants to say "run the smoke set" or "run the presubmit set" without repeating a long target list on every command line. A suite is a selection layer, not a new runner: Bazel expands it into underlying test targets and executes those targets through their existing runners1,2.

What actually runs when you test a suite?
Bazel expands //:ci_tests, expands any nested suite again, and runs only the resulting concrete test targets.
TOP-LEVEL SUITE
The command starts with one suite label
Bazel expands it before execution
//:ci_tests test_suite
DECLARED MEMBERS IN BUILD.bazel
:smoke_tests //integration:db_test
A test_suite selects tests. It does not start its own test process.
NESTED SUITE
This member is another suite
Bazel expands it again
//:smoke_tests
Concrete tests after expansion
BECOMES //integration:api_test //unit:fast_test
CONCRETE TEST
This member is already a test
Bazel runs this target
//integration:db_test
No more expansion is needed for this test-rule target.
No suite runner executes. tests(//:ci_tests) reveals the three test targets that do: //integration:api_test, //integration:db_test, and //unit:fast_test.
What does a suite control — and what stays with each test?
The suite gives a chosen set one stable label. Bazel executes each concrete member as its own test target.
TEST SUITE
One reviewed name for a chosen test set
the suite controls membership
//:ci_tests
Its member list can change in BUILD.bazel. People and CI keep using the same suite label.
TEST TARGET
Runs as its own test target
its tags, shard settings, and per-test artifacts
//unit:fast_test
TEST TARGET
Runs as its own test target
its tags, shard settings, and per-test artifacts
//integration:api_test
TEST TARGET
Runs as its own test target
its tags, shard settings, and per-test artifacts
//integration:db_test
Sharding
Splits one test target into several shard invocations.
Suite
Groups several test targets under one stable label.
Change the suite to change which tests belong. A command flag such as --test_output controls how Bazel displays their results.

The knobs from 1.2.3 Test Tags, 1.2.5 Test Sharding, and 1.2.7 Test Output & Debugging still apply at the member-test level. A suite changes how you name and collect tests, not how one test target behaves when Bazel finally runs it.

Prefer a named set over an ad-hoc pattern

0.2.3 Target Patterns already gave you broad selectors such as //... and //pkg/.... A test_suite is different: it is a real target in the build graph, declared once in BUILD.bazel, reviewed like any other target, and reused by name.

test_suite(
    name = "ci_tests",
    tests = [
        ":smoke_tests",
        "//integration:db_test",
    ],
)

That matters because suites can point at individual tests, nest other suites, and expand across package boundaries1. In the test-suites verification snippet, the root package defines only suite targets:

$ bazel query "kind(test, //:*)"
//:ci_tests
//:smoke_tests

But asking Bazel for the runnable tests behind the top-level suite expands it into the leaf tests, including tests from other packages:

$ bazel query "tests(//:ci_tests)"
//integration:api_test
//integration:db_test
//unit:fast_test

The suite label is the handle humans type, but the work still happens on the concrete member test targets Bazel expands underneath it1,3.

think

Compare: //slow:e2e_test has tags = ["manual"]. bazel test //... skips it, but //:ci_tests explicitly lists it in a test_suite. Which command runs the test, and what changes if the suite itself is tagged manual?

Reveal

bazel test //:ci_tests runs it because the suite names the target explicitly. The manual tag removes tests from wildcard expansion while leaving named tests reachable. That is the same idea as 1.2.3 Test Tags: manual changes discovery while the target remains runnable.

If the suite target itself is tagged manual, wildcard patterns skip the suite too. A direct bazel test //:ci_tests still means "run this named selection." That distinction is why suites are useful for CI: they can intentionally include tests that broad patterns avoid.

Suites Group Targets While Sharding Splits One

This is where test_suite and 1.2.5 Test Sharding diverge. Sharding takes one test target and runs it in multiple shard invocations. A suite does the opposite kind of composition: it gives several already-separate test targets one shared label.

So if the problem is "this single test target is too large", reach for sharding. If the problem is "people need one memorable name for these three test targets", reach for a suite.

Suites fit naturally into bazel test

The bazel test command accepts both test-rule targets and test_suite targets as test inputs2. That makes suite labels a good fit for human-facing commands such as:

bazel test //:smoke_tests
bazel test //:ci_tests --build_tests_only

--build_tests_only is a useful companion here because Bazel then limits top-level build work to the unfiltered concrete test targets and test_suite targets that survived the active size, timeout, tag, and language filters. Their production dependencies still build as needed2. Suites combine with the filters from 1.2.2 Test Sizes & Timeouts and 1.2.3 Test Tags rather than replacing them.

Use test_suite when the set is stable and human-chosen: smoke_tests, presubmit_tests, team_a_regression. Once the set needs to be derived from a diff, ownership graph, or monorepo-wide change analysis, the center of gravity moves toward query-driven selection in 5.2.7 Query in CI Pipelines and 6.5.1 Affected-Target Service Contract instead of maintaining ever-larger hand-written collector labels4,5.

extra

One advanced edge case: --expand_test_suites

When a test_suite is named on the command line, Bazel normally expands it much like a wildcard test pattern. In platform-aware builds, that means incompatible member tests are skipped under --expand_test_suites. Using --noexpand_test_suites changes the behavior: the suite target itself remains incompatible instead6.

key takeaway

Use a test_suite when people need one stable label for several existing tests. It groups and names real test targets. It does not introduce a separate execution model. Execution behavior stays on the tests inside it.

Check your understanding · 2 questions

1.True or false: test_suite behavior.

Choose True or False for each sentence

A suite gives several tests one stable label.
A suite creates a new test execution model separate from its members.
Suites may include tests from other packages and nested suites.

2.Which query expands a suite into the concrete tests it contains?

Select one answer

0 of 2 answered

Footnotes

  1. The Bazel Query Referencetests() expands test_suite targets into individual tests, applies tag/size filtering, and may return tests from other packages referenced directly or indirectly by suites 1 2 3

  2. Commands and Optionsbazel test accepts test-rule and test_suite targets, and --build_tests_only keeps only the unfiltered test targets needed for execution 1 2 3

  3. Query guide — practical tests(//foo:smoke_tests) examples, plus kind(...) and attr(...) queries for inspecting suite expansion

  4. Designing a language Agnostic CI using Bazel queries — uses tests() and kind() to derive the CI test stage from the Bazel graph

  5. How Bazel built its CI system on top of Buildkite — expands wildcard test requests into concrete target lists with bazel query, then shards that list across CI jobs

  6. Platforms--expand_test_suites skips incompatible member tests, while --noexpand_test_suites leaves the suite target itself incompatible