GitHub Actions
Record a map for every commit on the default branch, and let pull requests run only the tests their changes affect. The action builds TestDetta itself and keeps the maps in the Actions cache.
Workflow
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
actions: read # lets pull requests find the newest coverage map in the Actions cache
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7 # pin to a commit SHA
with:
fetch-depth: 0 # TestDetta compares with the merge base
persist-credentials: false
- uses: actions/setup-dotnet@v6 # pin to a commit SHA
with:
global-json-file: global.json
# Also the full test run of main. Leave out projects that cannot run here, such as
# integration tests that need a database; they are then decided by name on pull requests.
- if: github.event_name == 'push'
uses: <owner>/testdetta@<sha>
with:
mode: record
projects: |
tests/Shop.Tests/Shop.Tests.csproj
- if: github.event_name == 'pull_request'
uses: <owner>/testdetta@<sha>
with:
mode: test
dotnet-test-args: --logger trx
license: ${{ secrets.TESTDETTA_LICENSE }}
Full history is required. The action stops with an error on a shallow clone. Check out with fetch-depth: 0.
How the maps get to pull requests
- Record mode saves the maps in the Actions cache under
testdetta-coverage-<commit>. Pull requests can read caches of their base branch. - A pull request uses the map of its merge base, or the newest one recorded up to
max-map-agefirst-parent commits before it. - Finding that newer-than-exact map lists the repository's caches, which needs
actions: read. Without it, only a map at the merge base is found, and the step says so in a notice. - Without any map,
mode: teststill works, deciding by name only.
When tests fail while recording, the map is still saved before the job fails, so pull requests keep a map to work with.
Permissions
| Permission | Why |
|---|---|
contents: read | Check out the repository. |
actions: read | List the Actions caches to find the newest map within max-map-age. |
With persist-credentials: false a private repository cannot be fetched again later in the job; fetch-depth: 0 already brings every branch, so the base branch is there.
Inputs
| Input | Default | Description |
|---|---|---|
mode | test on pull requests (run affected tests), record on the default branch (record coverage maps). Required. | |
base | the PR's base branch | Branch to compare with in test mode. Outside pull requests, set it. |
configuration | Debug | Build configuration for building and running the tests. |
projects | all test projects | Record mode: .csproj paths, one per line. |
parallel | 1 | Record mode: test host processes per test project; each still runs its tests one at a time. The suite must tolerate several test processes at once. |
no-build | false | Record mode: true to record the build an earlier step made with the same configuration. |
dotnet-test-args | Test mode: extra dotnet test arguments, separated by spaces. | |
max-map-age | 50 | Test mode: commits a map may be behind the merge base; the changes in between are bridged. 0 uses only an exact map. |
drift-fallback | all | Test mode: when the changes since an older map include a build file, configuration or a global using, all runs the affected test projects completely; name decides them by name (faster, blind to calls made through configuration or reflection). |
github-token | github.token | Test mode: lists the Actions caches to find the newest map (needs actions: read). |
license | Test mode: the license string, from a repository or organization secret such as TESTDETTA_LICENSE. Public repositories need none. Without a valid license every test runs; the job never fails because of it. | |
working-directory | . | Any directory inside the repository to analyze. |
setup-dotnet | true | Install the .NET 10 SDK TestDetta runs on. Set to false if the job already has it. |
Job summary
In test mode, TestDetta writes its selection to the job summary: the changed files and the base it compared with, each test project with whether it ran filtered, completely or not at all and why, the test results, and the license state when it matters. Paths, class names and reasons from the repository are written as code, so file names in a pull request cannot inject links or mentions into the page.
Recording only part of the suite
Recording runs the whole suite once with coverage, which on the default branch is also your full test run. Leave out projects that cannot run on the runner, such as integration tests that need a database, with projects. Pull requests then decide those projects by name.
If an earlier step already built the solution with the same configuration, set no-build: true. For a suite that tolerates several test processes at once, parallel spreads each project's test classes over that many processes.