Skip to content
Development
Skill

/generate-testability-wrappers

Generate wrapper interfaces and DI registration for hard-to-test static dependencies in C#, when the abstraction does NOT exist yet. Produces IFileSystem, IEnvironmentProvider, IConsole, IProcessRunner wrappers, or guides first-time adoption of TimeProvider and

From plugin
dotnet-skills
5.1k96 skills16 agents
Install
$ npx -y skills add dotnet/skills --skill generate-testability-wrappers --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/generate-testability-wrappers

Context preview

The summary Claude sees to decide when to auto-load this skill.

Generate wrapper interfaces and DI registration for hard-to-test static dependencies in C#, when the abstraction does NOT exist yet. Produces IFileSystem, IEnvironmentProvider, IConsole, IProcessRunner wrappers, or guides first-time adoption of TimeProvider and

SKILL.md

generate-testability-wrappers.SKILL.md
name: generate-testability-wrappers
description: >
  Generate wrapper interfaces and DI registration for hard-to-test static dependencies in C#,
  when the abstraction does NOT exist yet. Produces IFileSystem, IEnvironmentProvider, IConsole,
  IProcessRunner wrappers, or guides first-time adoption of TimeProvider and IHttpClientFactory.
  With no DI container, produces the ambient context seam instead.
  USE FOR: generate wrapper for static, create IFileSystem wrapper, wrap DateTime.Now,
  make a static or a class testable, create abstraction for File.*, generate DI registration,
  adopt TimeProvider when it is not registered yet, IHttpClientFactory setup, testability
  wrapper, how to make statics injectable, adopt System.IO.Abstractions, make code testable
  without adding a DI framework.
  DO NOT USE FOR: detecting statics (use detect-static-dependencies), migrating
  call sites or replacing existing DateTime.*/File.* usages once the wrapper is created
  or already registered in DI (use migrate-static-to-wrapper), general interface design.
license: MIT

Generate Testability Wrappers

Generate wrapper interfaces, default implementations, and DI service registration code for untestable static dependencies. For statics that already have .NET built-in abstractions (`TimeProvider`, `IHttpClientFactory`), guide adoption of the built-in. For statics without built-in alternatives, generate custom minimal wrappers.

When to Use

  • After running `detect-static-dependencies` and identifying which statics to wrap
  • When the user asks to make a class testable by replacing statics with injected abstractions
  • When adopting `TimeProvider` (.NET 8+) or `System.IO.Abstractions`
  • When creating a custom wrapper for `Environment.*`, `Console.*`, or `Process.*`
  • When there is no DI container and the seam has to be ambient rather than injected

When Not to Use

  • The user wants to find statics first (use `detect-static-dependencies`)
  • The user wants to bulk-replace call sites (use `migrate-static-to-wrapper`)
  • The static is already behind an interface

> A project with **no DI container**, or a user who does not want to add one, is **not** a reason to skip this skill — > that is exactly what the ambient context seam in Step 5 is for. Choose the seam over constructor injection in that > case; do not decline the request and do not propose registering anything in a service collection.

Inputs

| Input | Required | Description | |-------|----------|-------------| | Static category | Yes | Which category: `time`, `filesystem`, `environment`, `network`, `console`, `process` | | Target framework | Yes | The `TargetFramework` from `.csproj` (affects which built-in abstractions exist) | | DI container | No | Which DI framework: `microsoft` (default), `autofac`, `none` (ambient context) | | Namespace | No | Target namespace for generated wrapper code |

Workflow

Step 1: Determine the abstraction strategy

Based on the category and target framework:

| Category | .NET 8+ | .NET 6-7 | .NET Framework | |----------|---------|----------|----------------| | Time | `TimeProvider` (built-in) | `TimeProvider` via `Microsoft.Bcl.TimeProvider` NuGet | Custom `ISystemClock` | | File system | `System.IO.Abstractions` (NuGet) | Same | Same | | HTTP | `IHttpClientFactory` (built-in) | Same | Same | | Environment | Custom `IEnvironmentProvider` | Same | Same | | Console | Custom `IConsole` | Same | Same | | Process | Custom `IProcessRunner` | Same | Same |

The table picks *which abstraction*. How it reaches the code under test is a separate axis: constructor injection when a DI container exists, and the **ambient context seam of Step 5** when one does not. Decide that axis first — check for a host builder, `IServiceCollection`, or an existing container registration — because a static class cannot take a constructor and a project without a container has nowhere to register anything. In that case skip Steps 2–4 and go to Step 5; the abstraction chosen above still applies, it is just reached through the ambient seam.

Step 2: Generate built-in abstraction adoption (Time, HTTP)

TimeProvider (.NET 8+)

No wrapper code needed — guide the user:

1. Register in DI:

builder.Services.AddSingleton(TimeProvider.System);

2. Inject into classes:

public class OrderProcessor(TimeProvider timeProvider)
{
    public bool IsExpired(Order order)
        => timeProvider.GetUtcNow() > order.ExpiresAt;
}

3. Test with `FakeTimeProvider`:

// Requires Microsoft.Extensions.TimeProvider.Testing NuGet
var fakeTime = new FakeTimeProvider(new DateTimeOffset(2026, 1, 15, 0, 0, 0, TimeSpan.Zero));
var processor = new OrderProcessor(fakeTime);
fakeTime.Advance(TimeSpan.FromDays(1));
Assert.True(processor.IsExpired(order));

TimeProvider (pre-.NET 8)

Guide: install `Microsoft.Bcl.TimeProvider` NuGet. Same API as above.

IHttpClientFactory

No wrapper code needed — register typed clients via `builder.Services.AddHttpClient<MyService>()` and inject `HttpClient` directly into the class constructor.

Step 3: Generate custom wrappers (Environment, Console, Process)

For categories without built-in abstractions, follow this template:

Interface — define the minimal surface

Only include methods that were actually detected in the codebase. Do NOT generate a wrapper for every possible member — wrap only what is used.

namespace <Namespace>;

/// <summary>
/// Abstraction over <static class> for testability. 
/// </summary>
public interface I<WrapperName>
{
    // One method per detected static call
    <return type> <MethodName>(<parameters>);
}

Default implementation — delegate to the real static

namespace <Namespace>;

/// <summary>
/// Default implementation that delegates to <static class>.
/// </summary>
public sealed class <WrapperName> : I<WrapperName>
{
    public <return type> <MethodName>(<parameters>)
        => <StaticClass>.<M
Read more
Ships withdotnet-skills

This repository contains the .NET team's curated set of core skills and custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io. 📊 Dashboard - Accuracy and efficiency scoring trends for contained plugins (

Get the whole plugin

Other skills on dotnet-skills.