Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Scan C# source files for hard-to-test static dependencies — DateTime.Now/UtcNow, File.*, Directory.*, Environment.*, HttpClient, Console.*, Process.*, and other untestable statics. Produces a ranked report of static call sites by frequency. USE FOR: find untestable statics, scan for static dependencies, testability audit, identify hard-to-mock code, find DateTime.Now usage, detect static coupling, testability report, static analysis for testability. DO NOT USE FOR: generating wrappers (use gener
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 1326% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 79% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 131% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 1512% | 0% |
Scan a C# codebase for calls to hard-to-test static APIs and produce a ranked report showing which statics appear most frequently, which files are most affected, and which abstractions already exist in the .NET ecosystem to replace them.
generate-testability-wrappers)migrate-static-to-wrapper)TimeProvider| Input | Required | Description | |-------|----------|-------------| | Target path | Yes | A file, directory, project (.csproj), or solution (.sln) to scan | | Exclusion patterns | No | Glob patterns to skip (e.g., **/obj/**, **/Migrations/**) | | Category filter | No | Limit to specific categories: time, filesystem, environment, network, console, process |
Resolve the target to a set of .cs files:
.cs file, scan that single file..cs files recursively (excluding obj/, bin/)..csproj, find its directory and scan .cs files within..sln, parse it, find all project directories, and scan .cs files across all projects.Always exclude obj/, bin/, and any user-specified exclusion patterns.
Scan each file for calls matching these categories:
| Category | Patterns to search for | Recommended replacement | |----------|----------------------|------------------------| | Time | DateTime.Now, DateTime.UtcNow, DateTime.Today, DateTimeOffset.Now, DateTimeOffset.UtcNow, Task.Delay(, new CancellationTokenSource(TimeSpan | TimeProvider (.NET 8+) | | File System | File.ReadAllText(, File.WriteAllText(, File.Exists(, File.Delete(, File.Copy(, File.Move(, Directory.Exists(, Directory.CreateDirectory(, Directory.GetFiles(, Directory.Delete(, Path.GetTempPath(, and instance members that hit the disk (new FileInfo(...), new DirectoryInfo(...), .LastWriteTimeUtc, new StreamReader(path)) | IFileSystem (System.IO.Abstractions NuGet) | | Randomness / identity | new Random(, Random.Shared, Guid.NewGuid( | TimeProvider-style seam: inject Random / an IGuidProvider | | Culture / serialization | CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture, JsonSerializer.Serialize(, JsonSerializer.Deserialize( | Pass culture/options explicitly, or inject a serializer abstraction | | Environment | Environment.GetEnvironmentVariable(, Environment.SetEnvironmentVariable(, Environment.MachineName, Environment.UserName, Environment.CurrentDirectory, Environment.Exit( | Custom IEnvironmentProvider | | Network | new HttpClient(, HttpClient.GetAsync(, HttpClient.PostAsync(, HttpClient.SendAsync( | IHttpClientFactory (built-in) | | Console | Console.WriteLine(, Console.ReadLine(, Console.Write(, Console.ReadKey( | IConsole wrapper or ILogger | | Process | Process.Start(, Process.GetCurrentProcess(, Process.GetProcessesByName( | Custom IProcessRunner |
Count each call site across the entire scan scope — including the instance-member call sites covered by the rules below, not only static ones.
Counting rules — inaccurate totals are the main way this report loses to an ad-hoc scan:
static. Instance members that reach the same untestable resource still count and belong in the matching category (new FileInfo(path).LastWriteTimeUtc → File System; httpClient.GetAsync(...) → Network). Say "hidden dependency", not "static", when the member is an instance call.Path.Combine, Path.GetExtension, Path.GetFileName, and Math.*/string.* statics take no ambient input and are trivially testable. List them, if at all, in a separate "no action needed" note — never as testability blockers.new Random(), Guid.NewGuid()), culture (CultureInfo.CurrentCulture), and serialization/statics such as JsonSerializer. Omitting a category that is present is an under-count.file:line for every occurrence so the user can jump straight to it.Produce a summary with:
TimeProvider (built-in since .NET 8)System.IO.Abstractions (NuGet package)IHttpClientFactory (built-in)IEnvironmentProviderIConsole or ILoggerIProcessRunnerFormat the output as a structured report:
## Static Dependency Report
**Scope**: <project/solution name>
**Files scanned**: <count>
**Total static call sites**: <count>
### Category Summary
| Category | Call Sites | Recommended Abstraction |
|-------------|-----------|------------------------|
| Time | 42 | TimeProvider (.NET 8+) |
| File System | 31 | System.IO.Abstractions |
| Environment | 12 | IEnvironmentProvider |
| ... | ... | ... |
### Top 10 Patterns
| # | Pattern | Count | Files |
|---|---------------------|-------|-------|
| 1 | DateTime.UtcNow | 28 | 14 |
| 2 | File.ReadAllText | 18 | 9 |
| ... |
### Most Affected Files
| File | Static Calls | Categories |
|-------------------------------|-------------|---------------------|
| Services/OrderProcessor.cs | 12 | Time, FileSystem |
| ... |
### Migration Priority
1. **Time** (42 sites) — Use `TimeProvider`, zero NuGet dependencies on .NET 8+
2. **File System** (31 sites) — Use `System.IO.Abstractions` NuGet package
3. ...Based on the report, recommend which category to tackle first (highest count, best built-in support). Keep this to a few lines.
Mention generate-testability-wrappers or migrate-static-to-wrapper only when the user's next action clearly needs them — a hand-off note, not a sales pitch. Never end an audit with promotional next-steps that dilute the findings.
.cs files in scope were scanned (check count)file:line locationPath.Combine, Math.*) are not counted as testability blockersobj/ and bin/ directories were excluded| Pitfall | Solution | |---------|----------| | Scanning obj/ or generated code | Always exclude obj/, bin/, and *.Designer.cs | | Counting wrapped calls as statics | Check if the call is behind an interface or injected service before counting | | Missing statics inside lambdas/LINQ | Search covers all code within .cs files, including lambdas | | Recommending TimeProvider on < .NET 8 | Check TargetFramework in .csproj — if < net8.0, recommend NodaTime.IClock or custom ISystemClock | | Ignoring test projects | Only scan production code — exclude *.Tests.csproj projects from the scan | | Under-counting by relegating findings | Real call sites belong in the category totals, not in a trailing "also noticed" paragraph that the totals ignore | | Calling an instance member a static | new FileInfo(p).LastWriteTimeUtc is an instance call but still a hidden file-system dependency — count it under File System and describe it accurately | | Recommending a wrapper for Path.Combine | Pure, deterministic helpers need no seam; listing them as blockers makes the recommendations wrong |
Other measured skills in the registry, with their headline benchmark lift.