Set up test impact analysis Beta
|
Smarter Testing is available in beta. This means the product is in early stages and you may encounter bugs, unexpected behavior, or incomplete features. When the feature is made generally available, there will be a cost associated with access and usage. Refer to our Discuss post for more information about our beta launch. |
Test impact analysis speeds up CI by running only the tests affected by your code changes. CircleCI tracks which tests exercise which source files, then skips tests that cover unchanged source files.
Is my project a good fit for test impact analysis?
This section outlines several project features that indicate your project is a good candidate for test impact analysis:
Built-in coverage support
Test impact analysis relies on code coverage data to determine which tests affect which files. Frameworks with built-in coverage support - Jest, pytest, Go test, Vitest and RSpec - make this straightforward.
If your framework has no native coverage support, generating the data test impact analysis needs may require significant rework of your test setup.
How it works
Test impact analysis is split into two phases:
-
Analysis runs on your default branch with coverage instrumentation, producing impact data. Impact data describes the relationship between files and tests.
-
Selection runs on feature branches, using the impact data to choose which tests to run based on what changed.
|
Analysis and selection can be configured to run on any change through CLI flags. |
Analysis on default branches
The analysis phase is run on your default branch (or the base branch of a pull request). It runs tests with coverage instrumentation and updates the impact data with the latest test-to-file relationships.
Keeping impact data fresh on the default branch allows the selection phase to select fewer tests without other unrelated changes interfering.
For each file found during coverage analysis, a fast non-crypto hash of its contents is stored in the impact data. The data also tracks which files impact the tests being selected.
{
"version": 1,
"files": {
"1": {
"path": "src/api/handlers.ts",
"hash": "c9684be83632a628"
},
"2": {
"path": "src/api/handlers.test.ts",
"hash": "5b8e1a04c7d2f391"
},
"3": {
"path": "src/data/repository.ts",
"hash": "2e7c4d18a9f6b052"
},
"4": {
"path": "src/data/repository.test.ts",
"hash": "f1d39e62a08c5b74"
}
},
"edges": {
"src/api/handlers.test.ts": ["1", "2", "3"],
"src/data/repository.test.ts": ["3", "4"]
}
}
-
filesrecords every file that analysis has seen, along with the hash of its contents at the time it was last analyzed. -
edgesrecords, for each test, the IDs of the files that are covered by the test.
|
The analysis phase typically runs slower than a normal test run because it executes tests with coverage instrumentation. However, this cost pays for itself by enabling the selection phase to skip unaffected tests on subsequent runs. |
Selection on feature branches
By default, all tests run on default branches. Test selection runs only affected tests on feature branches, using the impact data produced by analysis on the default branch.
During test selection, the current state of the branch’s checked-out code is compared against the latest impact data. Tests are selected in the following scenarios:
-
The test is not found in the impact data. This indicates a new test in the checked-out code.
-
The test failed on the previous run for this branch.
-
A file affecting the test is not found in the impact data. This indicates the file was removed from the checked-out code.
-
A file affecting the test hash has changed.
|
The selection rules can be extended to select tests beyond those identified in the impact data. |
Prerequisites
Before enabling test impact analysis, ensure you have completed the Getting Started With Smarter Testing guide and have:
-
Installed the
testsuiteCLI plugin. -
Configured your
.circleci/test-suites.ymlwithdiscoverandruncommands. -
Verified your tests run successfully with the
testsuitecommand.
1. Enable test impact analysis in your test-suites.yml file
Update your test suite configuration to include an analysis command and the test-impact-analysis option. Keep your existing discover and run commands.
Choose the starter configuration for your test runner and add the analysis line to your .circleci/test-suites.yml.
-
Vitest
-
Jest
-
Mocha
-
pytest
-
Go with gotestsum
-
RSpec
-
Other
Follow the instructions to add the Vitest CircleCI Coverage plugin as a dev dependency.
Add the analysis command to your .circleci/test-suites.yml.
---
name: ci tests
# ...
analysis: CIRCLECI_COVERAGE=<< outputs.circleci-coverage >> vitest run --silent --bail 0 << test.atoms >>
options:
test-impact-analysis: true
Follow the instructions to add the Jest CircleCI Coverage plugin as a dev dependency. The plugin is compatible with Jest 28 through 30. It requires jest, jest-environment-node, and jest-environment-jsdom as peer dependencies.
Add the analysis command to your .circleci/test-suites.yml.
---
name: ci tests
# ...
analysis: CIRCLECI_COVERAGE=<< outputs.circleci-coverage >> jest --runInBand --silent --bail << test.atoms >>
options:
test-impact-analysis: true
Follow the instructions to add the Mocha CircleCI Coverage plugin as a dev dependency.
Add the analysis command to your .circleci/test-suites.yml.
---
name: ci tests
# ...
analysis: CIRCLECI_COVERAGE="<< outputs.circleci-coverage >>" mocha << test.atoms >>
options:
test-impact-analysis: true
Follow the instructions to add the pytest CircleCI Coverage plugin as a dev dependency.
Add the analysis command to your .circleci/test-suites.yml.
---
name: ci tests
# ...
analysis: pytest --disable-pytest-warnings --no-header --quiet --tb=short --cov=myproj --cov-context=test --circleci-coverage=<< outputs.circleci-coverage >> << test.atoms >>
options:
test-impact-analysis: true
---
name: ci tests
# ...
file-mapper: go list -json="Dir,ImportPath,TestGoFiles,XTestGoFiles" ./... > << outputs.go-list-json >>
analysis: go tool gotestsum -- -coverprofile="<< outputs.go-coverage >>" -cover -coverpkg ./... << test.atoms >>
options:
test-impact-analysis: true
Follow the instructions to add the RSpec CircleCI Coverage plugin as a dev dependency.
Add the analysis command to your .circleci/test-suites.yml.
---
name: ci tests
# ...
analysis: CIRCLECI_COVERAGE="<< outputs.circleci-coverage >>" bundle exec rspec << test.atoms >>
options:
test-impact-analysis: true
Smarter Testing is test runner agnostic. Replace the analysis example below with your test runner. The next step validates that the commands are set up correctly.
---
name: ci tests
# ...
analysis: my-test-runner --coverage=<< outputs.coverage >> --run << test.atoms >>
options:
test-impact-analysis: true
Configure the test suite
Code coverage cannot detect every relationship between source files and test atoms. The following options let you extend test selection for edge cases.
For the full list of available options, see the Options section in the test suite configuration reference.
Full test run paths
Some project files affect the running system without being directly covered by tests. Examples include dependency manifests, database migration files, or CI configuration. Use full-test-run-paths to list files that cause all test atoms to be selected and run.
# .circleci/test-suites.yml
---
name: ci tests
# ...
options:
test-impact-analysis: true
full-test-run-paths:
- package.json
- go.mod
- .circleci/*.yml
- database-migrations/**/*.sql
Test selection rules
Use test-selection-rules to extend test selection to cover non-source files, or to always run specific test atoms. For example, run integration tests when database migrations change, or always run acceptance tests regardless of which files changed.
# .circleci/test-suites.yml
---
name: ci tests
# ...
options:
test-impact-analysis: true
test-selection-rules:
- test-atom: db/integration_test.ts
include: database-migrations/**/*.sql
- test-atom: acceptance/test.ts
include: true
2. Run locally
Use --doctor locally to validate the test-suites.yml is set up correctly. The CLI runs additional analysis checks when test impact analysis is enabled. If any results look incorrect, an action item is provided to resolve it.
$ circleci run testsuite "ci tests" --doctor
Follow the steps until all checks pass.
|
Run the The testsuite will automatically find the |
Once all checks pass, build the impact data locally. The following command runs coverage analysis on impacted test atoms without executing your test suite:
$ circleci run testsuite "ci tests" --verbose --local --select-tests=none --analyze-tests=impacted
This command:
-
--local— stores impact data in the.circleci/directory instead of fetching from the CircleCI API. -
--select-tests=none— skips running your test suite (no test results are produced). -
--analyze-tests=impacted— runs coverage analysis on test atoms whose impact data is outdated.
Inspect the locally stored impact data in .circleci/impact-ci tests.json to see the mapping between test atoms and files.
Next, modify a source file whose ID appears in the edges, then run test selection to verify only impacted test atoms are selected:
$ circleci run testsuite "ci tests" --verbose --local --select-tests=impacted
This command uses the existing impact data to select only the test atoms that cover the file you modified. Look for the "Selecting tests…" section in the output to confirm the correct tests are selected:
==> Selecting tests...
--> Selecting 'test-atom-one' due to modified file: 'src/foo.ts'
==> - 0 new test atoms
==> - 0 test atoms impacted by new files
==> - 1 test atoms impacted by modified files
==> - 0 test atoms impacted by removed files
==> - 0 test atoms failed previously
==> - 0 test atoms with no source file mappings in impact data
==> - 0 test atoms impacted by include rule
==> - 0 test atoms impacted by full test run paths
==> Selected 1 test atoms, Skipped 123 test atoms in 168ms
3. Run in CI
When adding the testsuite command to your CircleCI jobs, there is no need to install the testsuite plugin as it is already available in CircleCI Docker containers.
|
For most projects, no extra flags are needed — the defaults handle analysis and selection automatically. You only need the --select-tests and --analyze-tests flags if you want to verify analysis on your feature branch before merging, or if you need a non-default setup (see Common Setup Examples).
To verify analysis on your feature branch, temporarily override the defaults so the job only runs analysis without executing tests:
version: 2.1
jobs:
test:
executor: node-with-service
steps:
- setup
- run: circleci run testsuite "ci tests" --select-tests=none --analyze-tests=impacted
- store_test_results:
# This directory must match the directory of `outputs.junit` in your
# test-suites.yml
path: test-reports
This configuration skips running tests (--select-tests=none) and only runs coverage analysis (--analyze-tests=impacted), which lets you verify that analysis works before merging.
| Analysis must run on your default branch to keep impact data current. Once you have verified analysis on a feature branch, remove the flags to use the defaults, or see the Common Setup Examples section to dynamically change flags. |
Commit both .circleci/test-suites.yml and .circleci/config.yml to your feature branch and push to your VCS.
| Depending on the test runner, the first run of analysis can take a long time because every test atom needs to run analysis. Consider temporarily increasing the job parallelism on the feature branch. |
Once analysis has completed on the feature branch, restore the .circleci/config.yml back to its original state without the CLI flags and parallelism changes.
version: 2.1
jobs:
test:
executor: node-with-service
steps:
- setup
- run: circleci run testsuite "ci tests"
- store_test_results:
# This directory must match the directory of `outputs.junit` in your
# test-suites.yml
path: test-reports
Commit the restored .circleci/config.yml to your feature branch and push to your VCS. Follow your usual process to merge to your default branch.
Do not check in the local impact JSON files generated by the local CLI (for example, .circleci/ci tests-impact.json) to your VCS.
|
Verify in CI
After analysis completes on your feature branch, verify test impact analysis is working correctly:
-
In the CircleCI web app, navigate to your pipeline and open the test job.
-
Check that the job was successful and analyzed all test atoms. A successful analysis run outputs "Found n files impacting tests" for each test atom.
-
Modify a source file that appears in the impact data, then push. Only the test atoms that cover the modified file are selected to run. Look for the "Selecting tests…" output in the job to confirm the correct test atoms are selected and the reason for selection.
Test impact analysis is now set up for your test suite. Feature branches run the test atoms impacted by code changes, and your default branch runs all tests while also updating impact data.
If these defaults do not suit your project, see the Common Setup Examples section for alternative configurations.
Next steps
-
Use Dynamic Test Splitting to evenly split tests across parallel nodes.
-
Auto Rerun Failed Tests to automatically retry flaky tests.
Common setup examples
The --analyze-tests and --select-tests flags give you fine-grained control over how the testsuite command behaves. Most projects do not need to set these flags — the defaults work automatically.
Flag reference
--analyze-tests controls whether to build or update impact data by running tests with coverage instrumentation. --select-tests controls whether to choose and run tests based on existing impact data.
Each flag accepts three values:
| Value | Meaning for --analyze-tests |
Meaning for --select-tests |
|---|---|---|
|
Analyze only test atoms whose impact data needs updating — either the test changed, or the files it covers changed. |
Select and run only the test atoms impacted by a change. |
|
Skip analysis entirely. |
Skip running tests entirely. |
|
Analyze ALL discovered test atoms. Rarely needed — only use to rebuild impact data from scratch. |
Select and run all discovered test atoms (full test suite). |
When you do not pass these flags, the defaults depend on which branch is running:
| Branch | --analyze-tests default |
--select-tests default |
|---|---|---|
Default branch (for example, |
|
|
Feature branches |
|
|
By default, feature branches run only impacted tests, and the default branch runs all tests while also updating impact data.
Analyze impacted tests and run all tests on your default branch
No changes are required — the default behavior already handles both.
Analyze impacted tests as a non-blocking job in the same workflow
This approach runs analysis concurrently with the rest of your workflow jobs. It can reduce overall workflow time if analyzing tests takes longer than running tests.
# .circleci/config.yml
version: 2.1
jobs:
test:
executor: my-executor
parallelism: 4
steps:
- setup
# Disable analysis, run all tests on default branches, impacted tests on feature branches.
- run: circleci run testsuite "ci tests" --analyze-tests="none"
- store_test_results:
path: test-reports
analysis:
executor: my-executor
steps:
- setup
# Disable running tests, analyze impacted tests.
- run: circleci run testsuite "ci tests" --select-tests="none" --analyze-tests="impacted"
deploy:
executor: my-executor
steps:
- setup
- run: ./deploy.sh
workflows:
build-and-deploy:
jobs:
- test
# Only analyze tests on main.
- analysis:
filters pipeline.git.branch == "main"
- deploy:
requires:
- test
filters: pipeline.git.branch == "main"
Analyze impacted tests in a separate workflow in the same pipeline
This approach runs analysis concurrently with your main workflow, which is useful if you need to avoid any additional latency on your main workflow.
Only use this approach if analyzing impacted tests in a non-blocking job is not sufficient.
# .circleci/config.yml
version: 2.1
jobs:
test:
executor: my-executor
parallelism: 4
steps:
- setup
# Disable analysis.
# (Default) Run all tests on default branches, run impacted tests on feature branches.
- run: circleci run testsuite "ci tests" --analyze-tests="none"
- store_test_results:
path: test-reports
analysis:
executor: my-executor
steps:
- setup
# Disable running tests.
# Analyze impacted tests.
- run: circleci run testsuite "ci tests" --select-tests="none" --analyze-tests="impacted"
deploy:
executor: my-executor
steps:
- setup
- run: ./deploy.sh
workflows:
build-and-deploy:
jobs:
- test
- deploy:
requires:
- test
filters: pipeline.git.branch == "main"
analysis-workflow:
jobs:
# Only analyze tests on main.
- analysis:
filters: pipeline.git.branch == "main"
Analyze impacted tests on a non-default branch and run tests on all other branches
This approach is useful if you use a non-default branch as the base of development, for example in the "git flow" development model.
develop and selection on all other branches# .circleci/config.yml
version: 2.1
jobs:
test:
executor: node-with-service
parallelism: 4
steps:
- setup
# Analyze impacted tests on "develop" branch, otherwise disable.
# (Default) Run all tests on default branches, run impacted tests on feature branches.
- run: circleci run testsuite "ci tests" --analyze-tests=<< pipeline.git.branch == "develop" and "impacted" or "none" >>
- store_test_results:
path: test-reports