ScottPlot Test Runner
ScottPlot/ScottPlot
Run or add ScottPlot 5 tests. Use for unit-test and cookbook-test work; unless explicitly asked otherwise, restrict manual test execution to the Unit Tests…
MUST USE for C/.NET deterministic tests that require the smallest production seam for DateTime/Task.Delay/File/Environment/Guid/Random, static API preservation, nested/parallel overrides, or no real…
$ npx skills add dotnet/skills --skill testability-obstacle -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install dotnet/skills testability-obstacle --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/dotnet-test/skills/testability-obstacle .claude/skills/testability-obstacle && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "testability-obstacle" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacle into .claude/skills/testability-obstacle/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "testability-obstacle", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacleType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add dotnet/skills --skill testability-obstacle -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install dotnet/skills testability-obstacle --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/dotnet-test/skills/testability-obstacle .agents/skills/testability-obstacle && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "testability-obstacle" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacle into .agents/skills/testability-obstacle/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "testability-obstacle", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dotnet/skills --skill testability-obstacle -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install dotnet/skills testability-obstacle --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/dotnet-test/skills/testability-obstacle .cursor/skills/testability-obstacle && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "testability-obstacle" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacle into .cursor/skills/testability-obstacle/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "testability-obstacle", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/dotnet/skills.git --path plugins/dotnet-test/skills/testability-obstacle--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add dotnet/skills --skill testability-obstacle -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install dotnet/skills testability-obstacle --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/dotnet-test/skills/testability-obstacle .gemini/skills/testability-obstacle && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "testability-obstacle" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacle into .gemini/skills/testability-obstacle/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "testability-obstacle", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install dotnet/skills testability-obstacleInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add dotnet/skills --skill testability-obstacle -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/dotnet-test/skills/testability-obstacle .github/skills/testability-obstacle && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "testability-obstacle" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacle into .github/skills/testability-obstacle/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "testability-obstacle", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dotnet/skills --skill testability-obstacle -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install dotnet/skills testability-obstacle --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/dotnet-test/skills/testability-obstacle .opencode/skills/testability-obstacle && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "testability-obstacle" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-test/skills/testability-obstacle into .opencode/skills/testability-obstacle/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "testability-obstacle", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
testability-obstacleMUST USE for C/.NET deterministic tests that require the smallest production seam for DateTime/Task.Delay/File/Environment/Guid/Random, static API preservation, nested/parallel overrides, or no real…
Testability Obstacle is an agent skill from dotnet/skills, published by the product's own GitHub organization. MUST USE for C/.NET deterministic tests that require the smallest production seam for DateTime/Task.Delay/File/Environment/Guid/Random, static API preservation, nested/parallel overrides, or no real I/O. USE ONLY when the target workspace contains C source plus a .csproj or .sln. DO NOT USE for audits, bulk migration, code that already has an injectable seam, or an explicit migration to a user-named existing abstraction (migrate-static-to-wrapper). Use instead of general test generation when the requested test is…
Its SKILL.md is about 3.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Testing & QA, covering Test generation. It works with C# and .NET. The repository describes itself as: Repository for skills to assist AI coding agents with .NET and C. The licence is MIT.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 8d670fa. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are csharp).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Testability Obstacle loads about 3.6k tokens when it runs. Until then it costs about 153 tokens; SKILL.md has 1,848 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from dotnet/skills at commit 8d670fa, republished under its MIT licence (© dotnet). 1,848 words, ~3,599 tokens.
.claude/skills/testability-obstacle/SKILL.md (or your agent's skills folder).Introduce the smallest behavior-preserving seam needed to test a specific C# behavior, then add deterministic tests that prove both the behavior and the seam. The production edit is a means to the requested test, not an invitation to redesign adjacent code.
code-testing-agent.detect-static-dependencies.generate-testability-wrappers.migrate-static-to-wrapper, then generate tests separately.TimeProvider or
IFileSystem and asks to migrate call sites to it. Use
migrate-static-to-wrapper, which also updates affected tests.| Input | Required | Description |
|---|---|---|
| Behavior to test | Yes | The method/workflow and expected observable behavior |
| Target scope | No | Discover the narrowest relevant file/project when omitted |
| Allowed production changes | No | Default to the minimum internal/constructor seam |
Read the target production path and its existing tests. Identify the exact ambient operation preventing a deterministic test and the behavior that must remain unchanged. Do not run a repository-wide static scan for a single-class request.
If an adequate seam already exists, stop refactoring and use it. This skill adds no value when a fake can already be supplied.
Choose by dependency and repository constraints:
| Dependency | Preferred seam |
|---|---|
| Current time / timers | Inject TimeProvider; use FakeTimeProvider in tests |
| Filesystem | Existing repository abstraction; for one write/read operation use an injected delegate when conventions allow, otherwise a one-member interface or an already accepted System.IO.Abstractions |
| HTTP | Existing typed HttpClient/handler or IHttpClientFactory seam |
| Randomness | One final generated value: inject Func<int> and keep range selection in the real default; inject Func<int, int, int> only when range arguments are behavior the test must verify; multiple operations/state: inject Random or a minimal generator interface |
| Environment/console/process | Minimal interface containing only members used by the target |
The scoped AsyncLocal<T> rule applies to every static API that must retain its
public static shape — clocks, filesystem access, environment lookups, identity
generation, and randomness. The scope captures and restores the previous value;
never implement Dispose() as an unconditional assignment to null.
Store the provider/value itself in AsyncLocal<T>. Do not put a mutable
Stack<T>, list, or other shared mutable collection in the slot: child
execution contexts can inherit the same object and corrupt each other's nesting.
When the provider itself is mutable (for example an in-memory store or fake time
provider), establish a fresh provider inside each parallel flow rather than
mutating one inherited instance from a parent context.
Constructor injection is the default for instance classes. Reuse the repository's DI and naming conventions, but do not add a DI container to a class library just to satisfy this workflow.
Preserve the existing public construction surface unless the user authorizes an API change. Keep a public parameterless constructor as the real-dependency default and place a test-only delegate/provider constructor at the narrowest visibility the test project can reach. Do not turn the seam into a new public optional parameter merely for test convenience.
For a static class or a public API that cannot change, use a scoped ambient seam only when constructor/parameter injection is impossible. The override must:
await (AsyncLocal<T>, not [ThreadStatic]);IDisposable and restore the previous value, including nested scopes;Use built-in fake-time-aware overloads instead of inventing an IDelay wrapper:
| Ambient operation | Replacement |
|---|---|
Task.Delay(delay, token) | Task.Delay(delay, timeProvider, token) |
new CancellationTokenSource(delay) | new CancellationTokenSource(delay, timeProvider) |
PeriodicTimer(period) | new PeriodicTimer(period, timeProvider) when the target framework provides it |
Test delayed behavior by starting the operation, proving it is incomplete,
advancing FakeTimeProvider, then awaiting it. For a deadline or boundary,
advance to immediately before the deadline and assert the task is still
incomplete before advancing across it; an immediate post-start assertion alone
does not prove the boundary. Never wait for wall-clock time.
For a nested ambient override, each scope captures the value active when it
starts and restores that value exactly once. Dispose scopes in LIFO order with
using/finally; never reset the slot unconditionally to null. Tests must
observe the outer value after an inner scope ends normally and, when requested,
after an exception unwinds the inner scope. Use distinct values so clearing the
slot cannot accidentally pass. Also overlap independent async flows and assert
that each sees only its own fresh override. Do not mutate process environment
variables to test an environment seam.
var previous = s_provider.Value;
s_provider.Value = provider;
return new RestoreScope(() => s_provider.Value = previous);Keep the production change mechanical:
DateTime.Kind.Deterministic serialized text is a deliberate exception to preserving ambient
platform formatting. If the user asks for exact reproducible output across
platforms, use the format's explicit separator (use literal \n when none is
specified) and assert that literal content. Keep Environment.NewLine only
when platform-native output is part of the existing contract.
For time replacements:
DateTime.UtcNow -> timeProvider.GetUtcNow().UtcDateTimeDateTime.Now -> timeProvider.GetLocalNow().LocalDateTimeDateTimeOffset.UtcNow -> timeProvider.GetUtcNow()DateTimeOffset.Now -> timeProvider.GetLocalNow()Update every composition root or constructor call affected by the seam. Production
must still use real time/filesystem/etc. by default. If the project uses DI,
register the default implementation with the lifetime matching repository
conventions. If it does not use DI, compose explicitly; do not introduce a
container.
An existing manual factory must pass the real dependency explicitly (for example,
new ExpirationPolicy(TimeProvider.System)). Do not move responsibility into an
optional constructor or add an optional provider parameter to the factory.
Build the affected production project before writing tests. A compile failure here is a seam problem, not a test problem.
Use the repository's existing test project. If none exists, invoke
scaffold-dotnet-test-project first.
Tests must supply controlled dependencies:
Before authoring a test, inspect its test project and follow the existing framework,
global-using, and assertion conventions. Use the framework packages already referenced
by that project; never add a hand-rolled FactAttribute, a substitute test-framework
type, or unrelated test-project plumbing to make a test compile.
Assert the requested business result and at least one interaction/state observable that proves the fake dependency drove the path. Include a production-default test only when it can remain deterministic; never touch the real filesystem merely to prove the adapter delegates.
Cover every explicitly requested behavior and edge case. A theory or shared helper may keep the suite compact, but do not drop a case to minimize test count or replace retained tests with a smaller set. The seam should be minimal; the verification should still be complete.
Choose the narrowest seam that supports the behavior. A single
File.WriteAllText call can be an injected Action<string, string> with a real
default; do not create an interface, implementation, friend-assembly setting,
and extra project wiring unless repository conventions or multiple operations
justify them.
Preserve the public API surface as well as existing signatures. Do not add a
public dependency-injecting constructor solely for tests. When a class currently
has only its implicit public parameterless constructor and the exact test
assembly is known, keep that constructor behavior and make the test-only
constructor internal; an InternalsVisibleTo entry is justified in this narrow
case because it prevents the seam from becoming public API. Prefer an existing
repository friend-assembly convention when one is present.
Do not add InternalsVisibleTo when an existing public seam already accepts the
fake or the test project can otherwise supply it. Friend-assembly access is
justified only when the chosen minimum constructor/delegate seam must remain
internal to preserve the public API and the exact test assembly is known.
Run the affected production build and the narrowest targeted test command. Run a repository-level test command only when the user requested broad validation, the repository contract requires that entry point, or the seam changes shared composition used beyond the target. Re-read the diff and confirm:
Inspect the test summary, not only the exit code. Zero discovered tests, a build without the requested test run, or any failing/erroring test means the task is incomplete. Fix discovery/execution and rerun before reporting success. When a new test does not compile, correct its imports, assertion overload, or async test shape against the existing test framework before changing the production seam; do not emulate missing framework APIs in source. For a static ambient seam, completion requires executed tests for substitution, nested restoration, exception restoration when requested, and overlapping async-flow isolation; production compilation alone is never sufficient. Capture the passing test count or requested test names in the handoff. If no test was discovered or the output does not prove execution, correct the project/test source and rerun rather than reporting the seam as validated.
Provide a compact Requirement | Evidence table. Cite the production seam,
production default wiring, exact test names, and passing commands. If a package
restore or build blocks validation, report that blocker rather than claiming the
tests pass.
DateTime.Kind semantics.| Pitfall | Corrective action |
|---|---|
| Refactoring before proving a blocker | Reuse an existing seam and write the test directly |
| Wrapping an entire static API | Expose only members exercised by the target |
Converting UtcNow with .DateTime | Use .UtcDateTime to preserve DateTimeKind.Utc |
| Mutable static fake shared by tests | Use constructor injection or a scoped AsyncLocal<T> override |
| Adding DI to a library with no container | Compose the dependency explicitly |
| Using temp files as a shortcut | Supply an in-memory fake; the scenario requires no real I/O |
| Stopping after the refactor builds | Write and run the behavior tests that justified the seam |
| Reporting a zero-test run as success | Fix discovery and require the requested tests to execute and pass |
© dotnet, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in plugins/dotnet-test/skills/testability-obstacle of dotnet/skills.
Open the folder on GitHubat commit 8d670fa
We found 2 copies of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in dotnet/skills, which our catalogue first saw on October 7, 2026.
Testability Obstacle next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Testability Obstacle this skilldotnet/skills | 5.6k | 1 repos | ~3.6k | Automated safety check: Pass | MIT | |
| ScottPlot Test RunnerScottPlot/ScottPlot | 6.8k | — | ~308 | Automated safety check: Pass | MIT | |
| Macios CI Failure Inspectordotnet/macios | 2.9k | — | ~2.3k | Automated safety check: Pass | Custom licence | |
| Playwright Rollmicrosoft/playwright-dotnet | 3k | — | ~1.9k | Automated safety check: Pass | MIT | |
| Fix Random CI Test Failuredotnet/macios | 2.9k | — | ~1.3k | Automated safety check: Pass | Custom licence | |
| Test Firstdeanhume/html-minifier | 142 | — | ~876 | Automated safety check: Pass | MIT |
ScottPlot/ScottPlot
Run or add ScottPlot 5 tests. Use for unit-test and cookbook-test work; unless explicitly asked otherwise, restrict manual test execution to the Unit Tests…
dotnet/macios
Investigate and triage CI failures for dotnet/macios from Azure DevOps build URLs.
microsoft/playwright-dotnet
Roll Playwright .NET to a new version
dotnet/macios
Investigate and fix flaky/random CI test failures in dotnet/macios.
deanhume/html-minifier
Enforces a strict test-driven development (TDD) workflow for the HTML Minifier repo.
SSWConsulting/SSW.VerticalSliceArchitecture
A skill your agent uses when the user is working with an Aspire distributed application and needs to operate the AppHost or its resources through the Aspire CLI: start, restart, stop, or wait on the…
dotnet/skills
Resolves .NET runtime frames in Apple .ips crash logs to function names, source files and line numbers using dSYM symbols, atos and the Microsoft symbol server.
dotnet/skills
Resolves native crash frames from .NET Android tombstones to function names, source files and line numbers using BuildIds, Microsoft's symbol server and llvm-symbolizer.
dotnet/skills
Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.
dotnet/skills
Statically pairs source files with test files to list code that no test references, using Roslyn for C# or tree-sitter for many languages, with no build.
dotnet/skills
Activate this skill when BenchmarkDotNet (BDN) is involved in the task — creating, running, configuring, or reviewing BDN benchmarks.
dotnet/skills
Makes .NET projects compatible with Native AOT and trimming by resolving IL trim and AOT analyzer warnings through annotations rather than suppressions.
Categories
MUST USE for C/.NET deterministic tests that require the smallest production seam for DateTime/Task.Delay/File/Environment/Guid/Random, static API preservation, nested/parallel overrides, or no real…. Testability Obstacle is an agent skill from dotnet/skills, published by the product's own GitHub organization.Delay/File/Environment/Guid/Random, static API preservation, nested/parallel overrides, or no real I/O.
Testability Obstacle fits situations like: C/.NET deterministic tests that require the smallest production seam for DateTime/Task.Delay/File/Environment/Guid/Random; static API preservation; nested/parallel overrides; code that already has an injectable seam.
Run `npx skills add dotnet/skills --skill testability-obstacle -a claude-code`. Or copy the skill folder (plugins/dotnet-test/skills/testability-obstacle in dotnet/skills) into .claude/skills/testability-obstacle in your project. Claude Code loads it when a task matches its description.
Run `npx skills add dotnet/skills --skill testability-obstacle -a codex`. Or copy the skill folder (plugins/dotnet-test/skills/testability-obstacle in dotnet/skills) into .agents/skills/testability-obstacle in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add dotnet/skills --skill testability-obstacle -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/testability-obstacle, .gemini/skills/testability-obstacle, .github/skills/testability-obstacle and .opencode/skills/testability-obstacle in your project.
SKILL.md names no scripts, command-line tools or credentials: Testability Obstacle is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Testability Obstacle is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.6k tokens (SKILL.md is roughly 14k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Testability Obstacle: ScottPlot Test Runner (ScottPlot/ScottPlot, 6.8k stars), Macios CI Failure Inspector (dotnet/macios, 2.9k stars), Playwright Roll (microsoft/playwright-dotnet, 3k stars) and Fix Random CI Test Failure (dotnet/macios, 2.9k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
dotnet (a GitHub organization, an official publisher) maintains it in dotnet/skills, which has 5,568 GitHub stars. The repository holds 91 skills in this directory. The repository was last updated on October 7, 2026.
Source: dotnet/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.