DynaMock 1.0.2

dotnet add package DynaMock --version 1.0.2
                    
NuGet\Install-Package DynaMock -Version 1.0.2
                    
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="DynaMock" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DynaMock" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="DynaMock" />
                    
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 DynaMock --version 1.0.2
                    
#r "nuget: DynaMock, 1.0.2"
                    
#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 DynaMock@1.0.2
                    
#: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=DynaMock&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=DynaMock&version=1.0.2
                    
Install as a Cake Tool

DynaMock.NET

A source generator library for creating mockable wrappers around interfaces and abstract classes, enabling partial mocking and switching between real implementations and mocks at runtime.

Important: Design Philosophy & Intended Use

This library is designed exclusively for testing scenarios and should not be used in production code.

DynaMock.NET was created to address my (the author) specific needs when working with complex legacy codebases that lack proper dependency injection, testability, and clean architecture. It is not a replacement for good design principles and programming practices, or proper testing strategies.

NB: This package is currently considered stable for my needs, but certain features may be buggy, incomplete, or even be completely removed in future versions. I am not married to certain implementation and design choices and may change them for any reason, including the state of the weather on a particular day. I am also terrible at keeping documentation updated.

Things that are more likely to change than others:

  • Mock registration process
  • Adding/removing mocks
  • Numerous internal implementations (e.g. interceptors)
  • Testing methodologies

Consider using this library if:

  • You're working with legacy code that cannot be easily refactored
  • You need to write tests for tightly-coupled dependencies
  • You're dealing with static dependencies or singleton patterns
  • You need partial mocking for integration testing scenarios
  • Refactoring the relevant parts of the codebase is not feasible

Do not use this library:

  • As a substitute for proper interface design
  • In production/runtime code
  • As an excuse to avoid refactoring

What you should be doing

Your code should have proper dependency injection, clear interfaces, and be designed for testability from the start. DynaMock.NET exists for the real world where various constraints make this ideal unattainable.

Remember: This library is a pragmatic tool for dealing with less-than-ideal circumstances. It is not a pattern to emulate in new code.

Features

  • Partial Mocking: Mock only specific methods/properties while using real implementations for others
  • Runtime Switching: Toggle between real and mock implementations dynamically
  • Source Generated: Zero runtime reflection overhead
  • Thread-Safe Options: Choose between static or async-local mock providers
  • DI Integration: First-class support for Microsoft.Extensions.DependencyInjection
  • Interface & Abstract Class Support: Works with both interfaces and abstract classes
  • Event Support: Mock events alongside methods and properties

Installation

dotnet add package DynaMock

Quick Start

1. Mark Types for Mocking

Use the [Mockable] attribute to specify which types should have mockable wrappers generated:

using DynaMock;

[Mockable(typeof(IWeatherService))]
public class MockableTypes { }

public interface IWeatherService
{
    Task<string> GetWeatherAsync(string city);
    double GetTemperature();
    string Location { get; set; }
}

2. Register with Dependency Injection

var services = new ServiceCollection();

// Register your service first
services.AddTransient<IWeatherService, WeatherService>();

// Add mockable wrapper
services.AddMockable<IWeatherService>();

var provider = services.BuildServiceProvider();
var service = provider.GetService<IWeatherService>(); // Returns mockable wrapper

3. Use in Tests

Full Mocking (All Methods Use Mock)
// Arrange
var mock = Substitute.For<IWeatherService>();
mock.GetWeatherAsync("London").Returns("Sunny");
mock.GetTemperature().Returns(22.5);

// Set mock globally
DefaultMockProvider<IWeatherService>.SetMock(mock);

// Act
var weather = await service.GetWeatherAsync("London");
var temp = service.GetTemperature();

// Assert
Assert.Equal("Sunny", weather);
Assert.Equal(22.5, temp);

// Cleanup
DefaultMockProvider<IWeatherService>.RemoveMock();
Partial Mocking (Selective Methods)
var realService = new WeatherService { Location = "London" };
var mock = Substitute.For<IWeatherService>();
mock.GetWeatherAsync(Arg.Any<string>()).Returns("Mocked Weather");

// Only mock GetWeatherAsync, use real implementation for everything else
DefaultMockProvider<IWeatherService>.SetMock(mock, config => config
    .MockMethod(x => x.GetWeatherAsync(default!)));

var weather = await service.GetWeatherAsync("London"); // Returns "Mocked Weather"
var temp = service.GetTemperature(); // Uses real implementation
var location = service.Location; // Uses real implementation

Advanced Usage

Thread-Safe Mocking with AsyncLocal

For parallel tests or multi-threaded scenarios:

var provider = new AsyncLocalMockProvider<IWeatherService>();
var service = DynaMockFactory.Create(new WeatherService(), provider);

// Each async context gets isolated mock state
await Task.Run(async () =>
{
    var mock1 = Substitute.For<IWeatherService>();
    mock1.GetTemperature().Returns(25.0);
    provider.SetMock(mock1);
    
    var temp = service.GetTemperature(); // Returns 25.0
});

await Task.Run(async () =>
{
    var mock2 = Substitute.For<IWeatherService>();
    mock2.GetTemperature().Returns(30.0);
    provider.SetMock(mock2);
    
    var temp = service.GetTemperature(); // Returns 30.0
});

Mocking Properties

var mock = Substitute.For<IWeatherService>();
mock.Location.Returns("Paris");

DefaultMockProvider<IWeatherService>.SetMock(mock, config => config
    .MockProperty(x => x.Location));

service.Location = "Berlin"; // Sets on mock
var location = service.Location; // Returns "Paris" from mock

Mocking Events

var mock = Substitute.For<IWeatherService>();

DefaultMockProvider<IWeatherService>.SetMock(mock, config => config
    .MockEvent("WeatherChanged"));

var eventRaised = false;
service.WeatherChanged += (s, e) => eventRaised = true;

// Raise event on mock
mock.WeatherChanged += Raise.Event<EventHandler>(mock, EventArgs.Empty);

Assert.True(eventRaised);

Virtual Members Option

Generate virtual methods/properties in the wrapper itself for additional flexibility:

[Mockable(typeof(IMyService), VirtualMembers = true)]
public class MockableTypes { }

This allows the wrapper itself to be further mocked or inherited, useful for advanced testing scenarios.

Manual Instantiation

Without DI, create mockable instances directly:

var realImpl = new WeatherService();
var service = DynaMockFactory.Create<IWeatherService>(
    realImpl, 
    new DefaultMockProvider<IWeatherService>()
);

Configuration API

DefaultMockProvider<T>.SetMock(mock, config => config
    .MockMethod(x => x.MethodName())           // Mock specific method
    .MockMethod(x => x.AsyncMethod())          // Mock async methods
    .MockProperty(x => x.PropertyName)         // Mock specific property
    .MockEvent("EventName"));                  // Mock specific event

Access Real Implementation

Access the underlying real implementation even when mocking:

var mockable = service as MockableBase<IWeatherService>;
var realImpl = mockable.GetRealImplementation();

// Use in mock setup for spy-like behavior
mock.GetWeatherAsync(Arg.Any<string>())
    .Returns(async x => 
    {
        var realResult = await realImpl.GetWeatherAsync(x.Arg<string>());
        return $"[MOCKED] {realResult}";
    });

Supported Types

  • Interfaces
  • Abstract classes
  • Generic types
  • Async methods
  • Properties (get/set)
  • Events

Requirements

  • .NET 9.0 or later
  • Compatible with any mocking framework (NSubstitute, Moq, FakeItEasy, etc.)

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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. 
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.