CLI reference

Every command, option and environment variable, with what it does, its default and an example. Run td --help for the short version.

td affected [options]              Print the projects and tests a change affects
td test [options] [-- <args>]      Run only the tests a change affects
td record [options]                Run all tests with coverage and store the map
td license status [options]        Check the license and show what it covers
td license install <license|file>  Store a license in ~/.testdetta/license

Options for every analysis

These work with affected, test and record.

--base <ref>

The git ref to compare with. TestDetta compares your working tree with the merge base of HEAD and this ref, so commits that landed on the base branch after you branched off are not counted as your changes. Default: origin/main.

td test --base origin/develop
td affected --base HEAD~3      # the last three commits and anything uncommitted

--repo <path>

Any path inside the repository to analyse. Default: the current directory.

td affected --repo ../shop

-v, --verbose and --log-level <level>

Logs go to stderr, so stdout stays machine readable. -v turns on debug logs, which explain every decision: why a project is affected, which member a test class ran, which name a trace followed. --log-level picks any of trace, debug, information, warning (default), error or none; trace adds raw git output.

td affected -v 2> testdetta-debug.log
td test --log-level information

--max-map-age <n>

Use a coverage map recorded up to n first-parent commits before the merge base when there is none at the merge base itself. Changes made in between are bridged: classes that ran any member changed since the map and able to reach your change are selected too. 0 uses only a map recorded at the merge base. Default: 50. See How it decides.

td test --max-map-age 20

--drift-fallback <all|name>

What to do when the changes since an older map include something members cannot describe: a build file, configuration, a global using. all (default) runs the affected test projects completely; name decides them by name, which is faster but blind to calls wired by configuration or reflection.

td test --drift-fallback name

--maps-dir <dir>

Keep coverage maps outside the repository, one folder per commit. record saves to <dir>/<commit>/; affected and test restore the newest one within --max-map-age. Use it on CI systems other than GitHub Actions, with a shared disk or synced object storage. Default: maps live in .testdetta/coverage in the repository.

td record --maps-dir /srv/testdetta-maps/shop
td test --base origin/main --maps-dir /srv/testdetta-maps/shop

td affected

Prints what a change affects and why, without building or running anything.

--format <text|json|paths|commands>

FormatOutputUse it for
text (default)Affected projects with the reason, then the test planReading
jsonEvery project and test class with its decision, evidence and time estimateScripts, dashboards
pathsOne affected test .csproj per lineFeeding another tool
commandsOne dotnet test command per test project, with its filterRunning the tests your own way
td affected --format json > plan.json
td affected --format paths | xargs -n1 dotnet build

--tests-only

List only the affected test projects, not the production projects on the way.

td test

Builds and runs only the affected tests: a filter per test project when only some classes are affected, the whole project when all are or when a change cannot be narrowed, nothing when none is. Exit code 3 when any test fails.

--dry-run

Print the dotnet test commands instead of running them. A project whose global.json lives in a subfolder is printed as (cd src && dotnet test ...).

--summary-file <path>

Append a Markdown summary: each project's result, which classes ran and why, which were skipped, and the recorded test time saved. Point it at $GITHUB_STEP_SUMMARY in GitHub Actions; the action does this for you. See an example.

td test --summary-file "$GITHUB_STEP_SUMMARY"

-- <dotnet test arguments>

Everything after -- is passed to every dotnet test call.

td test -- -c Release --logger trx
td test -- --no-restore

td record

Runs every test with coverage and writes one map per test project to .testdetta/coverage (or --maps-dir). Run it on your default branch; it is also that branch's full test run. Exit code 3 when tests fail; the maps are still written, and the failing classes are always selected later.

-c, --configuration <name>

Build configuration. Default: Debug.

-f, --framework <tfm>

Target framework to record in a multi-targeted test project. Default: the project's first target framework.

td record -f net10.0

--project <path>

Record only this test project, relative to the repository root; repeat it for several. Leave out projects that cannot run in CI, such as integration tests that need a database; they are then decided by name.

td record --project tests/Shop.Tests/Shop.Tests.csproj --project tests/Api.Tests/Api.Tests.csproj

--parallel <n>

Spread each project's test classes over n test processes to record faster. Each process still runs its tests one at a time. Your suite must tolerate several test processes at once (no fixed ports or shared files). Default: 1.

--no-build

Record the output of an earlier build with the same configuration instead of building again.

dotnet build -c Release
td record -c Release --no-build

td license

td license status checks the license the way td test would and prints what it covers: licensee, seats, active committers over the last 30 days, the bot list, scopes, the end date. It sends no personal data anywhere. td license install <license|file> stores a license in ~/.testdetta/license, for machines where no CI secret can hold it. See License.

Environment variables

VariableWhat it does
TESTDETTA_LICENSEThe license string, from a CI secret.
TESTDETTA_LICENSE_FILEA file holding the license string.
TESTDETTA_LICENSE_REFRESH=offTurns off the daily license refresh, for networks that cannot reach the license service.
TESTDETTA_LICENSE_SCOPEThe repository the license must cover, when the origin remote does not name it (a local mirror, for example).
HTTPS_PROXYProxy for the license refresh.

Exit codes

CodeMeaning
0Success, including "no tests needed".
1The analysis failed: git, the file system or a project file.
2Invalid command line.
3Tests failed (test and record).