MoqExtensions.ReflectionProxy 1.0.0

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

Moq.ReflectionProxy

Unofficial extension for Moq that enables method call proxying to real implementations using reflection, with comprehensive interception capabilities.

🚀 Features

  • Partial Mocking: Forward specific method calls to real implementations while keeping other methods mocked
  • Method Interception: Comprehensive logging, testing, and behavior modification capabilities
  • Reflection-Based Setup: Streamlined mock configuration using MethodInfo or lambda expressions
  • Seamless Integration: Works perfectly with existing Moq workflows and patterns
  • Flexible Forwarding: Support for both selective and complete method forwarding

📦 Installation

dotnet add package MoqExtensions.ReflectionProxy

🔧 Quick Start

Partial Mocking with Method Forwarding

Forward Specific Methods

Forward individual methods to their real implementations while keeping others mocked:

var realService = new UserService(/* dependencies */);
var mock = new Mock<IUserService>();

// Forward only GetUserById to the real implementation
mock.Setup(x => x.GetUserById(It.IsAny<int>()))
    .ForwardTo(realService);

// Other methods remain mocked
mock.Setup(x => x.CreateUser(It.IsAny<CreateUserRequest>()))
    .Returns(new User { Id = 123 });
Forward All Methods (example with Dependency Injection)

Automatically forward all interface methods to a real implementation:

serviceCollection.AddScoped<IUserService>(provider => {
    var mock = new Mock<IUserService>();
    
    // Add custom setups here if needed
    mock.Setup(x => x.GetUserById(It.IsAny<int>()))
        .Throws<Exception>();
    
    // Forward all other calls to real implementation
    return mock.DefaultForwardTo(provider.GetRequiredService<UserService>()).Object;
});

Advanced Interception

Add sophisticated interception logic for logging, monitoring, and behavior modification:

var interceptor = new MethodInterceptor(
    onInterceptEntry: context =>
    {
        Console.WriteLine($"Calling {context.FromMethod.Name} with {context.ParameterValues.Count} arguments");

        // Optional: Override method behavior
        if (context.FromMethod.Name == "GetUserById" && (int)context.ParameterValues[0]! == 42)
        {
            return new InterceptSubstitution
                { ByValue = new OptionalNullable<object>(new User { Id = 42, Name = "ReplacedUser" }) };
        }

        // Store additional context for later interceptors
        context.AdditionalContext = "some context";

        return null; // Continue with normal execution
    },
    onInterceptException: context =>
    {
        Console.WriteLine($"Exception in {context.FromMethod.Name} after {context.GetElapsedTime()}ms: " +
                            $"{(context.IsUnwrapTask ? context.UnwrapException! : context.Exception!).Message}");
    },
    onInterceptValue: context =>
    {
        // context.AdditionalContext
        var result = context.IsUnwrapTask ? context.UnwrapReturnTaskValue : context.ReturnValue.Value;
        
        Console.WriteLine($"{context.FromMethod.Name} completed in {context.GetElapsedTime()}ms, " +
                            $"returned: {result}");
    }
);

mock.SetupFunction(x => x.GetUserById)
    .ForwardTo(realService, interceptor);

📚 Reflection-Based Setup

Replace Moq setup with reflection-based setup configuration:

Action Methods (void return)

// Traditional Moq approach
mock.Setup(x => x.SaveCity(It.IsAny<City>(), It.IsAny<CancellationToken>()))
    .Callback<City, CancellationToken>((city, token) => {
        // callback logic
    });

// Reflection-based approach
mock.SetupAction(x => x.SaveCity)
    .Callback<City, CancellationToken>((city, token) => {
        // callback logic
    });

Function Methods (with return values)

// Traditional Moq approach
mock.Setup(x => x.SearchCity(It.IsAny<string>(), It.IsAny<Zone>(), It.IsAny<CancellationToken>()))
    .ReturnsAsync(new City { Name = "Paris" });

// Reflection-based approach
mock.SetupFunction(x => x.SearchCity)
    .HasReturnType<City>()
    .ReturnsAsync(new City { Name = "Paris" });

Complex Method Scenarios

For methods with overloads, generic parameters, or ambiguous signatures, use the explicit MethodInfo approach:

// Handle complex method signatures explicitly
var searchMethod = typeof(IUserService).GetMethod(nameof(IUserService.SearchUsers));

mock.SetupFunction(searchMethod)
    .Returns(new List<User>());

📖 API Reference

Moq Extension Methods

Method Description
ForwardTo Forward mock calls to a real implementation
DefaultForwardTo Forward all interface methods to target
SetupAction Setup void methods via reflection
SetupFunction Setup methods with return values via reflection

Interception

Component Description
IMethodInterceptor Interface for implementing custom interceptors
MethodInterceptor Built-in delegate-based interceptor implementation
InvocationContext Rich context object containing method and execution information
InterceptSubstitution Return object to override default method behavior

Context Properties

The InvocationContext provides comprehensive information about method invocations

⚠️ Current Limitations

  • Methods with ref and out parameters are not currently supported
  • Generic method constraints are not fully validated at setup time

📄 License

This project is licensed under the MIT License - see the LICENSE file for complete details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • Moq (>= 4.20.72)

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
1.0.0 83 7/28/2025