如何让.NET Core依赖注入自动返回未注册服务的Moq实例?
Great question! This is exactly the kind of scenario where a little DI customization can save you tons of repetitive mock registration code. Yes, you absolutely can set up a fallback provider that auto-generates Mock<T> instances for any unregistered services when resolving from the ServiceProvider.
Here's a clean, straightforward way to implement this:
Step 1: Create a Fallback Service Provider Wrapper
We'll build a custom IServiceProvider that wraps the default DI container. When a service isn't found in the registered services, it will automatically generate a mock using Moq (but only for interfaces/abstract classes, since Moq can't mock concrete types without additional setup).
using Microsoft.Extensions.DependencyInjection; using Moq; public class MockFallbackServiceProvider : IServiceProvider { private readonly IServiceProvider _innerProvider; public MockFallbackServiceProvider(IServiceProvider innerProvider) { _innerProvider = innerProvider; } public object? GetService(Type serviceType) { // First try to resolve from the original registered services var registeredService = _innerProvider.GetService(serviceType); if (registeredService != null) { return registeredService; } // If no registration exists, create a mock for interfaces/abstract types if (serviceType.IsInterface || serviceType.IsAbstract) { var mockFactoryMethod = typeof(Mock) .GetMethod(nameof(Mock.Of))? .MakeGenericMethod(serviceType); return mockFactoryMethod?.Invoke(null, null); } // For unregistered concrete types, return null (or throw if you prefer strict behavior) return null; } }
Step 2: Use the Fallback Provider in Your Setup
Now you only need to register your core services (like SomeService), and let the fallback handle all unregistered dependencies automatically:
// Register only your main services (no need to register every dependency!) var services = new ServiceCollection() .AddTransient<ISomeService, SomeService>(); // Build the default inner provider var innerProvider = services.BuildServiceProvider(); // Wrap it with our mock fallback provider var serviceProvider = new MockFallbackServiceProvider(innerProvider); // Resolve your service - unregistered dependencies will be auto-mocked! var someService = serviceProvider.GetRequiredService<ISomeService>();
Key Notes & Customization Options
- Strict vs. Lenient Behavior: The current setup returns
nullfor unregistered concrete types. If you want to enforce that only mocked interfaces/abstracts are allowed, you can throw an exception here instead. - Filter Specific Types: If you want to exclude certain interfaces from being auto-mocked, add a check in the
GetServicemethod (e.g.,if (serviceType == typeof(ISensitiveInterface)) return null;). - Mock Customization: If you need default mock behavior (like returning specific values), you can extend the logic to configure the mock before returning it (using
Mock.Get(mockInstance)to access the mock object and set up methods/properties).
This approach cuts down on boilerplate significantly, especially for classes with dozens of dependencies—no more manually registering every single mock!
内容的提问来源于stack exchange,提问作者Daniel James Bryars

