MockInterceptor 1.0.0-alpha.0
dotnet add package MockInterceptor --version 1.0.0-alpha.0
NuGet\Install-Package MockInterceptor -Version 1.0.0-alpha.0
<PackageReference Include="MockInterceptor" Version="1.0.0-alpha.0" />
<PackageVersion Include="MockInterceptor" Version="1.0.0-alpha.0" />
<PackageReference Include="MockInterceptor" />
paket add MockInterceptor --version 1.0.0-alpha.0
#r "nuget: MockInterceptor, 1.0.0-alpha.0"
#:package MockInterceptor@1.0.0-alpha.0
#addin nuget:?package=MockInterceptor&version=1.0.0-alpha.0&prerelease
#tool nuget:?package=MockInterceptor&version=1.0.0-alpha.0&prerelease
🎣 MockInterceptor: Non-Intrusive Mocking for Coupled Code
MockInterceptor is a C# 12 Source Generator library designed to enable dependency mocking of tightly coupled classes. By strictly adhering to the C# Interceptors method call specification, it rewrites Intermediate Language (IL) at compile time, redirecting targeted method calls to a mock proxy managed by your test framework (e.g., Moq).
💡 The Problem Solved
In tightly coupled code, internal instantiation and static calls hinder unit testing:
public class Worker
{
private readonly Executor executor = new();
public (string, int) DoWork() =>
(Executor.GetVersion(), this.executor.Execute());
}
MockInterceptor can bypasses the actual Executor code execution by transparently redirecting all method calls (GetVersion, Execute) to your mock setup.
🚀 Configuration: The Dedicated Test Build
To maintain a clean separation between development, testing, and production, we use a custom Test build configuration. This ensures the interception code is only compiled when running tests, avoiding overhead and maintaining a clean debugging experience for your regular Debug and Release builds.
1. Define the Custom Test Configuration
Ensure your solution file (.slnx) defines the Test build type alongside Debug and Release
<Configurations>
<BuildType Name="Debug" />
<BuildType Name="Test" />
<BuildType Name="Release" />
</Configurations>
2. Configure the Target Project (.csproj)
The Target Project (containing Worker and Executor) must define the required constants and reference the generator only when the configuration is explicitly set to Test.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<InternalsVisibleTo Include="Your.Test.Project" />
</PropertyGroup>
<ItemGroup Condition="'$(Configuration)' == 'Test'">
<PackageReference Include="MockInterceptor" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
✍️ Usage Guide
1. Marking the Target Class
Guard the [Intercept] assembly attribute with the #if TEST constant to ensure it's only included in your special test build.
File: AssemblyConfig.cs (in the Target Project)
#if TEST
using MockInterceptor;
// Tells the generator to process the Executor class and all its method call sites.
[assembly: Intercept(typeof(Executor))]
#endif
2. The Interception Mechanism Explained
The Source Generator emits interfaces and the static ExecutorInterceptor class to manage the mocks.
| Target Code | Interception Target | Mock Management Strategy |
|---|---|---|
Static Call (Executor.GetVersion()) |
The GetVersion() method call. |
Redirected to the StaticMock property (IStaticExecutor). |
Instance Call (executor.Execute()) |
The Execute() method call. |
Redirected to a generated Extension Method that checks the mock state. |
Constructor (new Executor()) |
No Direct Interception. | The mock (from MockQueue) is consumed and tracked by the first instance method call (Execute()) on the newly created object. |
3. Writing the TestIn your Test Project, use Moq with the generated interfaces and control the redirection via the ExecutorInterceptor's static properties
// Guarding the test class with #if TEST because ExecutorInterceptor is generated only in the test build
#if TEST
using MockInterceptor.Interceptors; // Generated namespace
using Moq;
using Xunit;
namespace MockInterceptor.Sample.Tests;
public class InterceptorTest
{
[Fact]
public void MockStaticAndInstance()
{
// 1. Reset state (Essential for test isolation)
ExecutorInterceptor.Reset();
// 2. Create Mocks using the generated interfaces
var instanceMock = new Mock<IExecutor>();
var staticMock = new Mock<IStaticExecutor>();
// 3. Setup mock expectations
instanceMock.Setup(x => x.Execute()).Returns(42);
staticMock.Setup(x => x.GetVersion()).Returns("v5");
// --- STAGING THE MOCKS ---
// 4. Stage the Static Mock
ExecutorInterceptor.StaticMock = staticMock.Object;
// 5. Queue the Instance Mock (Consumed by the first instance method call)
ExecutorInterceptor.MockQueue.Enqueue(instanceMock.Object);
// 6. Act (Worker instantiation and calls trigger the Interceptors)
var worker = new Worker();
var actual = worker.DoWork();
// 7. Assert
var expected = ("v5", 42);
Assert.Equal(expected, actual);
// Verify the instance mock was called
instanceMock.Verify(x => x.Execute(), Times.Once);
}
}
#endif
| Product | Versions 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. |
-
.NETStandard 2.0
- No dependencies.
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-alpha.0 | 107 | 12/20/2025 |