Skip to content

Repository files navigation

Impact

Live unit testing for .NET in VS Code — free and open source.

Save a file, see the affected tests go green (or red) in ~50 milliseconds. No build, no test-host startup, no running the whole suite. The closest thing to this is Visual Studio's Live Unit Testing, which requires an Enterprise license and doesn't exist for VS Code at all.

Break a method, save, watch the affected test go red in under 100ms — fix it, save, green again.

Why it's fast

Most "run tests on save" tools pay the full toll every time: MSBuild spin-up, test-host spawn, suite discovery — seconds of ceremony before a single assert runs. Impact removes the toll booths:

  • Hot patching — your edit is compiled into an Edit-and-Continue delta (the same engine behind dotnet watch hot reload) and injected into already-running test hosts. Method bodies, new methods, new fields, new types, lambdas — no build at all.
  • Warm test sessions — pre-warmed testhosts stay alive between runs, so even when a real build is needed, running the tests costs milliseconds of dispatch instead of seconds of startup.
  • Affected-test selection — an IL-based impact map (refined by measured coverage as tests run) knows which test classes can reach the file you changed, so only those run.
  • Shadow worktree — everything happens in a background git worktree mirror of your repo, so background builds never fight your editor over bin/obj.

Adding a brand-new method and calling it is still a hot patch — the test goes red in 48ms because the method you just wrote is already running inside the warm test host:

Add a new method and call it — the affected test runs the new code in 48ms, no build.

Every run logs an honest timing line to the Impact output channel: timing: fastpath=hit build=0ms tests=12ms total=36ms — and when the fast path can't be used (a changed public signature, a rude edit), it says exactly why and falls back to a minimal rebuild.

Getting started

  1. Install Impact from the VS Code Marketplace, open a .NET repo with test projects.
  2. The Testing panel populates with your tests (classes and methods) and an impact map builds in the background.
  3. Live testing is on: save a .cs file and affected tests run automatically. The eye in the Testing toolbar pauses and resumes it — open eye = live, crossed eye = paused. Pausing cancels the in-flight run at once; resuming runs whatever changed while paused. Every new window starts live. (Impact: Toggle live testing in the command palette does the same.)

Works with xUnit, NUnit, and MSTest, both classic VSTest projects and Microsoft.Testing.Platform apps (xunit.v3, MSTest.Sdk, the native NUnit/MSTest runners). Coverage runs (native VS Code coverage view) are built in via the Coverage profile; Impact: Run affected tests now runs everything uncommitted on demand.

The first run on a repo pays a one-time setup: a full build, the impact map, and a small local build of the helper services. After that, saves are fast.

CLI (pre-commit hooks / AI agents)

The same engine ships as a CLI for hooks and coding agents:

impact build-map                  # build or refresh the map (background/overnight)
impact build-map --if-missing     # no-op when a map already exists (CI warmer)
impact affected [file ...]        # print affected test classes (--format json for scripts)
impact run [file ...]             # run affected tests; exit 1 on failure
impact run --staged               # pre-commit mode (index only)
impact run --base <ref>           # everything the branch changed
impact status                     # map size and learned bindings

With no files and no --base/--staged, affected and run use the dirty tree (everything uncommitted).

Exit codes are the contract: 0 pass/nothing affected, 1 failure, 2 usage. Infrastructure never blocks a commit — no map yet or shadow busy warns and exits 0. Pre-commit via lint-staged ({ "*.cs": "impact run" }) or a plain hook with impact run --staged.

For an AI coding agent, impact run <files> after each edit gives sub-minute feedback scoped to the blast radius of the change. Put that in your repo's agent docs (CLAUDE.md or equivalent) so agents discover it.

In CI, the same selection turns a PR build into an affected-only run: impact run --base <PR base> --ci (where --ci fails instead of soft-skips on missing infrastructure), with the map restored from a cache and build-map --if-missing as the no-op warmer. Full recipe with a GitHub Actions workflow: docs/ci.md.

How selection works

  1. Impact map — built statically in seconds from the assemblies' IL metadata and portable PDBs: each test class's transitive type-reference closure becomes source file → test classes. Measured per-class coverage replaces the static rows as tests actually run, converging on observed truth (including DI/reflection edges static analysis can't see).
  2. On save — changed files are looked up in the map; mapped files run just those test classes, unmapped files fall back to every test project that transitively references the changed project.

Known blind spots (by design)

  • Reflection / DI indirection: a config change that reroutes DI can affect tests the map doesn't predict. Non-.cs and unmapped files fall back to project-level selection.
  • .csproj / config edits use the project-graph fallback, not the map.
  • A changed public/internal signature takes the rebuild path unless every project that consumes it is already in the hot-patch session (has a baseline from an earlier build). With all consumers present the engine recompiles them as part of the same delta; with any consumer missing it refuses, because a hot patch would leave that assembly green against an API that no longer compiles.

Settings

All under dotnetImpact.*; defaults shown.

Setting Default What it does
debounceMs 1500 Quiet time after the last save before affected tests run.
watchExternalChanges true Also run on changes that land on disk without a save (git checkout/pull, other editors).
externalDebounceMs 1000 Burst window for those external changes: one git operation becomes one run.
persistentTestSessions true Keep warm testhosts alive between runs (the hot-patch fast path needs this).
autoBuildMap true Build/refresh the impact map in the background when unmapped test classes exist.
liveMapRefresh true After each run, re-measure coverage for the classes that ran to keep map rows fresh.
warmCoverageRefresh true Collect that coverage through a resident dotnet-coverage session (installed on first use).
learnedBindings true Apply binding edges learned from coverage (DI implementations etc.) when selecting tests; only ever adds tests.
surfaceBuildWarnings "auto" Show build warnings as squigglies; auto = only when the C# extension isn't installed. Errors always show.
testProjectGlobs ["**/*Test*.csproj", "**/*.Tests.csproj"] Which csproj files count as test projects.
dotnetPath "" Explicit dotnet executable; empty = auto-detect.
maxParallelCoverageRuns 0 Parallel --list-tests discovery runs; 0 = cores minus 2, max 12.

Development

npm install
npm run compile     # or: npm run watch
npm test            # unit tests (node --test)

Launch with F5 (Extension Development Host). To try a build in your normal VS Code instead: npx @vscode/vsce package then code --install-extension impact-*.vsix --force and reload the window.

All state lives under ~/.impact/<repo>-<hash>/ — delete that folder to reset everything (then git worktree prune in the repo).

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages