aspnet-core
Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on…
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
$ npx -y skills add managedcode/dotnet-skills --skill testability-obstacle --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/testability-obstacleContext preview
The summary Claude sees to decide when to auto-load this skill.
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
name: testability-obstacle description: >- 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 impossible without a production edit and seam selection is still open. license: MIT
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.
process, or another ambient dependency.
a fake through the existing seam using `code-testing-agent`.
`detect-static-dependencies`.
`generate-testability-wrappers`.
`migrate-static-to-wrapper`, then generate tests separately.
`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 generated value: injected delegate with `Random.Shared` as the production default; 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:
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 pos
Stop explaining .NET to your AI. Start building. We've all been there: asking Claude to use Entity Framework, only to get EF6 patterns in a .NET 8 project. Explaining to Copilot that Blazor Server and Blazor WebAssembly aren't the same thing.
Repo: managedcode/dotnet-skills
Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on…
Build, upgrade, and operate Aspire 13.5.x C# or TypeScript application hosts with the current CLI, AppHost, ServiceDefaults, integrations, dashboard, testing,…
Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable Functions patterns. USE FOR:…
Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and…
Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. USE…
Design, tune, or review EF Core data access with proper modeling, migrations, query translation, performance, and lifetime management for modern .NET…