1.2.3 Test Tags

Test metadata does two different jobs in Bazel. Most teams use ordinary strings such as unit, integration, lint, or smoke so --test_tag_filters can carve the repository into useful lanes. A smaller built-in set changes Bazel's own behavior: manual changes wildcard expansion, exclusive changes scheduling, external changes caching, and requires-network matters when tests need an escape hatch from stricter sandbox policy1,2,3.

Why did this test not run — or how will it run?
Bazel first selects test targets. Special metadata then changes how selected tests execute.
Follow one test: //app:api_test with tags = ["unit", "requires-network"].
Example request selection → execution
bazel test //... --test_tag_filters=unit,-flaky
1 · REQUEST
Does the target pattern include it?
//... includes //app:api_test
Result Candidate
2 · MANUAL
Is it excluded from wildcards?
no "manual" tag
Result Still selected
3 · FILTER
Do its category tags pass?
unit ✓   flaky absent ✓
Result Test will run
4 · EXECUTE
Does metadata change execution?
requires-network
Result Run with network access
manual removes a test from wildcard requests. An explicit label such as bazel test //db:manual_test still selects it.
Execution modifiers: exclusive runs alone. external skips result caching. requires-network allows network access under a restricted policy. flaky = True enables retry attempts if the test is selected.
To explain a missing test, inspect request expansion, manual exclusion, then tag filters. Execution modifiers matter only afterward.

Tags Are Not Sizes or Dependency Boundaries

Do not mix test tags with 1.2.2 Test Sizes & Timeouts or 1.2.4 testonly Attribute. Size and timeout tell Bazel how heavy a test is. testonly constrains who may depend on a target. Tags answer a different question: should this test be selected for this invocation, or does Bazel need to treat it specially once it runs?1,2

That is also why tags and 1.2.6 Test Suites are complementary rather than interchangeable. A suite gives you a named bundle. Tags stay attached to individual targets and let the CLI slice across package boundaries, which is why teams usually encode categories like unit, integration, or smoke in tags and keep suites for stable named sets2,3.

manual Happens Before Tag Filters

0.2.3 Target Patterns already covered the headline rule: manual removes a target from wildcard patterns such as //..., :all, and :*. The operator-important consequence is that manual is not a normal category tag you can recover later with --test_tag_filters. Wildcard expansion happens first, and only then does Bazel apply test filters to the surviving test targets1,2.

The target-patterns snippet reproduces the behavior with a real java_test marked tags = ["manual"]:

$ bazel query //...
//app:lib
//app:lib_test
//app:server

$ bazel test //...
INFO: Found 2 targets and 0 test targets...
ERROR: No test targets were found, yet testing was requested

$ bazel test //... --test_tag_filters=manual
INFO: Found 2 targets and 0 test targets...
ERROR: No test targets were found, yet testing was requested

bazel query //... still sees the target, because query is an inspection tool rather than a wildcard test runner1. But once you ask Bazel to execute tests selected by //..., the manual target is already gone from the request set. If you really want it, you must name it explicitly, for example bazel test //app:lib_test.

This is the practical boundary between manual and ordinary classification tags. Use manual when a test should stay out of all broad sweeps by default. Use normal tags when the test should remain part of wildcard-based workflows but move between lanes such as PR, integration, or nightly runs.

Use --test_tag_filters For Normal Categories

--test_tag_filters is the right tool for project vocabulary. The flag keeps tests that match at least one positive keyword and rejects any test carrying an excluded keyword2. That gives you commands like:

bazel test //... --test_tag_filters=unit,-flaky
bazel test //... --test_tag_filters=integration,-external

This scales better than encoding CI policy in target names or directories. The BUILD file stays close to the test, while the CLI decides which slice to run today3,4. It also keeps broad commands readable: one repo can expose fast lint and unit lanes, slower integration lanes, and occasional smoke sweeps without inventing a new suite for every combination1,3.

Built-In Modifiers Trade Convenience For Control

Some keywords tell Bazel that a target breaks the normal fast-parallel-cached assumptions instead of merely grouping it1,3.

exclusive means "run no other test at the same time"1. Reach for it when tests fight over a shared emulator, container, database, or other singleton resource3. It is a scheduling escape hatch, so it should be rare.

flaky lives in the same operator conversation, and you can filter on -flaky from the CLI, but BUILD files usually express the retry behavior with the dedicated flaky = True attribute2,3. Bazel then allows three attempts by default for such tests, while --flaky_test_attempts=N lets CI raise or override that retry policy for a whole invocation2,4. Retries limit a flaky test's immediate impact. They do not fix the underlying flake. H.7.7 Flakiness explains how teams assign responsibility and quarantine unreliable tests.

external disables test-result caching for tests that depend on external state1. That makes it useful for cases where rerunning is safer than pretending the result is reproducible.

extra

requires-network Only Makes Sense Under A Stricter Policy

requires-network is best understood as an escape hatch for hermetic teams, not as a default mode for integration tests. It becomes meaningful when you deliberately tighten sandbox networking, typically with --sandbox_default_allow_network=false, and then opt specific tests back into network access5,6.

That is why this keyword really points forward to 2.3 Hermeticity & Sandboxing and 3.2.6 Hermeticity Settings. The tag provides a narrow exception when the repository keeps network access off by default. It does not bless general non-determinism.

The Level 1 skill is to separate these categories cleanly: manual for wildcard exclusion, ordinary tags for repo-specific selection, and built-in modifiers only when you need a scheduling, retry, caching, or networking exception. Later, H.7.2 Evidence Portfolio turns that same vocabulary into repository-wide policy about what should run on every change and what should stay in slower lanes.

key takeaway

Use manual when wildcards should ignore a test entirely. Use ordinary tags plus --test_tag_filters when you want selectable test lanes. Treat exclusive, flaky, external, and requires-network as escape hatches that trade some of Bazel's default parallelism, caching, or hermeticity for a specific operational need.

Check your understanding · 3 questions

1.True or false: how does the manual tag interact with wildcards and --test_tag_filters?

Choose True or False for each sentence

You can recover a test tagged 'manual' by running bazel test //... --test_tag_filters=manual.
Wildcard expansion excludes manual targets first, then tag filters apply to the remaining set.
The manual tag is removed from targets before --test_tag_filters runs.
--test_tag_filters only supports custom tags, not built-in keywords.

2.Match each built-in test tag to the behavior it modifies:

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
manual
exclusive
external
requires-network

3.Which flags help you narrow the tests Bazel plans to run, without also building explicitly named non-test targets?

Select all that apply

0 of 3 answered

Footnotes

  1. Test encyclopedia — special test tag conventions, manual wildcard exclusion, bazel query ignoring manual, exclusive, external, and smoke 1 2 3 4 5 6 7 8

  2. Commands and Options--test_tag_filters, --build_tests_only, filtering semantics, and --flaky_test_attempts 1 2 3 4 5 6 7 8

  3. How to set up a Bazel testing configuration: comprehensive guide for Scala and Java — practical custom-tag workflows, --test_tag_filters examples, exclusive, and flaky = True 1 2 3 4 5 6 7

  4. BazelCon 2019 Day 2: Building a great CI w/ Bazel — CI practice for excluding manual, using --flaky_test_attempts, and pairing wide test sweeps with --build_tests_only 1 2 3

  5. Bazel Training 101 (Part 15): Flags and Tags — operator-facing summary of special test keywords, including requires-network

  6. .bazelrc flags you should enable--sandbox_default_allow_network=false and the role of requires-network as an opt-out