PatchStatic 1.0.0

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package PatchStatic --version 1.0.0
                    
NuGet\Install-Package PatchStatic -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PatchStatic" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PatchStatic" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="PatchStatic" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PatchStatic --version 1.0.0
                    
#r "nuget: PatchStatic, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PatchStatic@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PatchStatic&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=PatchStatic&version=1.0.0
                    
Install as a Cake Tool

PatchStatic User Guide

PatchStatic is a C# library that enables mocking of static methods and properties for unit testing purposes. It uses Harmony to patch static methods at runtime, providing thread-safe mocking with automatic cleanup through scoped mocking patterns.

Table of Contents

Key Features

  • Static Method Mocking: Mock static methods and properties
  • Thread-Safe: Each thread maintains isolated mock state
  • Scoped Mocking: Automatic cleanup when scope is disposed
  • Property Support: Mock static properties through their getter methods
  • Global Reset: Ability to clear all patches globally
  • Expression-Based: Type-safe method selection using lambda expressions

Basic Usage

Simple Method Mocking

The library includes a sample StaticTimeProvider class that demonstrates typical static method patterns:

public static class StaticTimeProvider
{
    public static DateTime Now => DateTime.Now;
    
    public static bool IsBusinessHours()
    {
        var hour = Now.Hour;
        return hour is >= 9 and < 17;
    }
}

Here's how to mock it in your tests:

[Fact]
public void SimpleMethodMocking_IsBusinessHours_ReturnsMockedValue()
{
    Mock.Scope(scope =>
    {
        // Mock the static property to return a specific time
        var mockNow = scope.Static(() => StaticTimeProvider.Now);
        mockNow.Returns(new DateTime(2025, 6, 9, 10, 0, 0));
        
        // Test the business logic that depends on the mocked time
        Assert.True(StaticTimeProvider.IsBusinessHours());
    });
    
    // Outside the scope, the original behavior is restored
    var realNow = StaticTimeProvider.Now;
    Assert.IsType<DateTime>(realNow);
}

Property Mocking

You can mock static properties directly:

[Fact]
public void PropertyMocking_NowReturnsMockedDateTime()
{
    Mock.Scope(scope =>
    {
        var mockNow = scope.Static(() => StaticTimeProvider.Now);
        var fakeTime = new DateTime(2025, 6, 9, 20, 0, 0);
        mockNow.Returns(fakeTime);
        
        // The mocked value is returned
        Assert.Equal(fakeTime, StaticTimeProvider.Now);
    });
    
    // After scope disposal, original behavior is restored
    Assert.NotEqual(new DateTime(2025, 6, 9, 20, 0, 0), StaticTimeProvider.Now);
}

Advanced Features

Multiple Mocks in One Scope

You can mock multiple static methods within the same scope:

[Fact]
public void MultipleMocksInOneScope_WorkCorrectly()
{
    Mock.Scope(scope =>
    {
        // Mock the Now property
        var mockNow = scope.Static(() => StaticTimeProvider.Now);
        mockNow.Returns(new DateTime(2025, 6, 9, 11, 0, 0));
        
        // Mock the IsBusinessHours method directly
        var mockIsBusinessHours = scope.Static(() => StaticTimeProvider.IsBusinessHours());
        mockIsBusinessHours.Returns(false);
        
        // Both mocks are active
        Assert.Equal(new DateTime(2025, 6, 9, 11, 0, 0), StaticTimeProvider.Now);
        Assert.False(StaticTimeProvider.IsBusinessHours());
    });
    
    // All mocks are cleaned up automatically
    Assert.IsType<DateTime>(StaticTimeProvider.Now);
}

Thread Isolation

Each thread maintains its own mock state, ensuring thread safety:

[Fact]
public void BasicThreadIsolation_MocksAreThreadSpecific()
{
    var mainThreadId = Environment.CurrentManagedThreadId;
    var otherThreadId = -1;
    
    Mock.Scope(scope =>
    {
        // Mock only affects the current thread
        var mockNow = scope.Static(() => StaticTimeProvider.Now);
        mockNow.Returns(new DateTime(2025, 6, 9, 10, 0, 0));
        
        Assert.True(StaticTimeProvider.IsBusinessHours());
        
        // Other threads see the original behavior
        var task = Task.Run(() =>
        {
            otherThreadId = Environment.CurrentManagedThreadId;
            return StaticTimeProvider.IsBusinessHours();
        });
        
        var otherResult = task.Result;
        
        // Verify threads are different
        Assert.NotEqual(mainThreadId, otherThreadId);
        
        // Other thread result is based on actual time, not mocked time
        Assert.True(otherResult || !otherResult);
    });
}

Concurrent Testing with Parallel.For

Test concurrent scenarios using Parallel.For:

[Fact]
public void RaceConditionTesting_DifferentMocksInEachThread()
{
    var exceptions = new System.Collections.Concurrent.ConcurrentQueue<Exception>();
    
    Parallel.For(0, 10, i =>
    {
        try
        {
            Mock.Scope(scope =>
            {
                // Each thread gets a different mocked time
                var mockedTime = new DateTime(2025, 6, 9, 10, 0, 0).AddHours(i);
                var mockNow = scope.Static(() => StaticTimeProvider.Now);
                mockNow.Returns(mockedTime);
                
                var now = StaticTimeProvider.Now;
                Assert.Equal(mockedTime, now);
            });
        }
        catch (Exception ex)
        {
            exceptions.Enqueue(ex);
        }
    });
    
    // Verify no exceptions occurred
    Assert.Empty(exceptions);
}

Concurrent Testing with Async Tasks

Test concurrent scenarios using async tasks:

[Fact]
public async Task RaceConditionTesting_DifferentMocksInEachThread_UsingAsyncTasks()
{
    var exceptions = new System.Collections.Concurrent.ConcurrentQueue<Exception>();
    var tasks = new List<Task>();
    
    for (var i = 0; i < 10; i++)
    {
        var localIndex = i;
        var task = Task.Run(() =>
        {
            try
            {
                Mock.Scope(scope =>
                {
                    // Each task gets its own mocked time
                    var mockedTime = new DateTime(2025, 6, 9, 10, 0, 0).AddHours(localIndex);
                    var mockNow = scope.Static(() => StaticTimeProvider.Now);
                    mockNow.Returns(mockedTime);
                    
                    var now = StaticTimeProvider.Now;
                    Assert.Equal(mockedTime, now);
                });
            }
            catch (Exception ex)
            {
                exceptions.Enqueue(ex);
            }
        });
        tasks.Add(task);
    }
    
    await Task.WhenAll(tasks);
    
    // Verify no exceptions occurred in any task
    Assert.Empty(exceptions);
}

API Reference

Mock Class

public static class Mock
{
    // Create a new mock scope
    public static void Scope(Action<MockScope> action)
    
    // Global cleanup - removes all patches (use sparingly)
    public static void GlobalResetAll()
}

MockScope Class

public class MockScope : IDisposable
{
    // Create a mock for a static method or property
    public StaticMethodMock<TResult> Static<TResult>(Expression<Func<TResult>> methodExpr)
}

StaticMethodMock<TResult> Class

public class StaticMethodMock<TResult>
{
    // Set the return value for the mocked method
    public StaticMethodMock<TResult> Returns(TResult value)
}

It Class (Parameter Matching)

public static class It
{
    // Match any parameter of type T
    public static T? IsAny<T>() => default;
}

Limitations

  • Can only mock public static methods and properties
  • Cannot patch readonly static methods or properties
  • Does not support nested scopes (scope within scope)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated