Microsoft.Extensions.DependencyInjection integration for MoqProxy. Enables wrapping DI-registered services with Moq proxies for testing - verify calls to real implementations, spy on service interactions, and test integration scenarios without modifying production code.
$ dotnet add package geoder101.MoqProxy.DependencyInjection.MicrosoftMicrosoft.Extensions.DependencyInjection integration for MoqProxy - enabling proxy pattern mocking within ASP.NET Core and other dependency injection scenarios.
This library provides extension methods to seamlessly integrate MoqProxy with Microsoft's dependency injection container. It allows you to wrap existing service registrations with Moq proxies for testing, enabling you to:
dotnet add package geoder101.MoqProxy.DependencyInjection.Microsoft
using Microsoft.Extensions.DependencyInjection;
using Moq;
// Set up your services as usual
var services = new ServiceCollection();
services.AddSingleton<IEmailService, EmailService>();
services.AddSingleton<IUserService, UserService>();
// Wrap a service with a Moq proxy for testing
var emailMock = new Mock<IEmailService>();
services.AddMoqProxy(emailMock);
// Build and use the container
var provider = services.BuildServiceProvider();
var userService = provider.GetRequiredService<IUserService>();
// The UserService will receive the proxied EmailService
userService.RegisterUser("john@example.com");
// Verify the real EmailService was called through the proxy
emailMock.Verify(e => e.SendWelcomeEmail("john@example.com"), Times.Once);
AddMoqProxy<TService>() decorates an existing service registration with a Moq proxy using the decorator pattern:
This means:
var services = new ServiceCollection();
services.AddSingleton<ICalculator, Calculator>();
var mock = new Mock<ICalculator>();
services.AddMoqProxy(mock);
var provider = services.BuildServiceProvider();
var calculator = provider.GetRequiredService<ICalculator>();
// Calls are forwarded to the real Calculator implementation
var result = calculator.Add(2, 3); // Returns 5
// But you can still verify the call
mock.Verify(c => c.Add(2, 3), Times.Once);var services = new ServiceCollection();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IBusinessLogic, BusinessLogic>(); // Depends on IRepository
// Proxy the repository to observe calls from BusinessLogic
var repoMock = new Mock<IRepository>();
services.AddMoqProxy(repoMock);
var provider = services.BuildServiceProvider();
var businessLogic = provider.GetRequiredService<IBusinessLogic>();
// Execute business logic that uses the repository
businessLogic.ProcessOrder(orderId: 123);
// Verify the repository was called correctly
repoMock.Verify(r => r.GetOrder(123), Times.Once);
repoMock.Verify(r => r.SaveOrder(It.IsAny<Order>()), Times.Once);var services = new ServiceCollection();
services.AddSingleton<IPaymentGateway, StripePaymentGateway>();
var mock = new Mock<IPaymentGateway>();
services.AddMoqProxy(mock);
// Override specific behavior for testing
mock.Setup(p => p.IsAvailable()).Returns(false);
var provider = services.BuildServiceProvider();
var gateway = provider.GetRequiredService<IPaymentGateway>();
// Overridden behavior
Assert.False(gateway.IsAvailable()); // Returns false from setup
// Other calls still forward to StripePaymentGateway
gateway.ProcessPayment(amount: 100); // Calls real implementationpublic class IntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
[Fact]
public async Task Registration_ShouldSendEmail()
{
// Arrange
var emailMock = new Mock<IEmailService>();
var factory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
// Proxy the email service
services.AddMoqProxy(emailMock);
});
});
var client = factory.CreateClient();
// Act
var response = await client.PostAsJsonAsync("/api/users/register", new { Email = "test@example.com" });
// Assert
response.EnsureSuccessStatusCode();
emailMock.Verify(e => e.SendWelcomeEmail("test@example.com"), Times.Once);
}
}var services = new ServiceCollection();
services.AddSingleton<ILogger, ConsoleLogger>();
services.AddSingleton<ICache, RedisCache>();
services.AddSingleton<IApi, ExternalApi>();
// Proxy multiple services
var loggerMock = new Mock<ILogger>();
var cacheMock = new Mock<ICache>();
var apiMock = new Mock<IApi>();
services.AddMoqProxy(loggerMock);
services.AddMoqProxy(cacheMock);
services.AddMoqProxy(apiMock);
var provider = services.BuildServiceProvider();
// All services are now proxied and verifiable
// ... run your test scenario ...
// Verify all interactions
loggerMock.Verify(l => l.Log(It.IsAny<string>()), Times.AtLeastOnce);
cacheMock.Verify(c => c.Get(It.IsAny<string>()), Times.Once);
apiMock.Verify(a => a.FetchData(), Times.Once);TService must already be registered in the IServiceCollectionpublic static IServiceCollection AddMoqProxy<TService>(
this IServiceCollection services,
Mock<TService> mock)
where TService : classParameters:
services - The service collection containing the service to proxymock - The Moq mock instance that will wrap the original implementationReturns: The IServiceCollection for method chaining
Remarks:
AddMoqProxySetupAsProxy()Contributions are welcome! Please feel free to submit issues or pull requests.
This project is licensed under the MIT License - see the LICENSE.txt file for details.
This repository is part of an ongoing exploration into human-AI co-creation.
The code, comments, and structure emerged through dialogue between human intent and LLM reasoning — reviewed, refined, and grounded in human understanding.