如何用Simple Injector拦截所有实例以实现集成测试?
Simple Injector集成测试中监控内部服务调用的解决方案
方案1:用FakeItEasy包装真实实例替换目标服务
这种方式直接替换容器中目标服务的注册,让所有依赖该服务的地方(包括隐式创建的依赖)都使用带Spy的包装实例,从而记录所有调用。
- 步骤:
- 先注册服务的真实实现
- 在测试环境下,用
RegisterConditional替换为FakeItEasy创建的Spy包装器,包装真实实例 - 将Spy实例保存到测试类的可访问字段中,用于后续验证调用
示例代码:
// 基础容器配置:注册真实服务 var container = new Container(); container.Register<IMyService, MyRealService>(Lifestyle.Scoped); // 测试环境下替换为Spy包装 bool isTesting = true; // 测试时设为true if (isTesting) { container.RegisterConditional(typeof(IMyService), context => { // 获取真实实例 var realService = context.GetInstance<MyRealService>(); // 创建包装真实实例的Spy,保留原有逻辑同时记录调用 var serviceSpy = A.Fake<IMyService>(opt => opt.Wrapping(realService)); // 保存Spy到测试类的静态字段或测试实例字段 TestContext.MyServiceSpy = serviceSpy; return serviceSpy; }, Lifestyle.Scoped, // 条件:所有请求都替换 context => true); } // 测试验证部分 A.CallTo(() => TestContext.MyServiceSpy.Process(A<RequestModel>.That.Matches(r => r.Id == 123))) .MustHaveHappenedOnceExactly();
方案2:增强ApplyInterceptor获取服务类型
如果坚持使用拦截器方案,可以通过自定义拦截器传递服务类型,解决ApplyInterceptor中无法获取服务类型的问题。
- 步骤:
- 实现带服务类型参数的自定义拦截器,包装核心拦截逻辑(比如FakeItEasy的调用记录)
- 在
ApplyInterceptor注册时,将当前的服务类型传递给自定义拦截器
示例代码:
// 自定义拦截器,携带服务类型信息 public class TrackedInterceptor : IInterceptor { public Type ServiceType { get; } private readonly IInterceptor _innerInterceptor; public TrackedInterceptor(Type serviceType, IInterceptor innerInterceptor) { ServiceType = serviceType; _innerInterceptor = innerInterceptor; } public void Intercept(IInvocation invocation) { // 此处可访问ServiceType,用于区分不同服务的调用 Console.WriteLine($"监控到{ServiceType.Name}的{invocation.Method.Name}方法调用"); _innerInterceptor.Intercept(invocation); } } // FakeItEasy拦截器实现,用于记录调用 public class FakeItEasyInterceptor : IInterceptor { private readonly object _target; public FakeItEasyInterceptor(object target) => _target = target; public void Intercept(IInvocation invocation) { // 用FakeItEasy拦截调用并记录 var fake = A.Fake(invocation.Target.GetType(), opt => opt.Wrapping(_target)); var method = fake.GetType().GetMethod(invocation.Method.Name); method.Invoke(fake, invocation.Arguments); // 这里可以自定义记录逻辑,比如保存调用参数到测试上下文 } } // 容器注册拦截器 container.ApplyInterceptor( typeof(IMyService), (context, instance) => new TrackedInterceptor( context.ServiceType, new FakeItEasyInterceptor(instance)));
方案3:直接替换InstanceProducer的实例
通过Simple Injector的InstanceProducer直接替换目标服务的实例,确保所有依赖该服务的地方都使用Spy实例。
示例代码:
var container = new Container(); container.Register<IMyService, MyRealService>(Lifestyle.Scoped); // 获取目标服务的InstanceProducer var producer = container.GetRegistration(typeof(IMyService)).Registration; // 创建真实实例并包装为Spy var realInstance = producer.BuildInstance(); var serviceSpy = A.Fake<IMyService>(opt => opt.Wrapping(realInstance)); // 替换Producer中的实例 producer.ReplaceInstance(serviceSpy); // 测试验证 A.CallTo(() => serviceSpy.DoWork(A<string>.That.IsEqualTo("test"))).MustHaveHappened();
内容的提问来源于stack exchange,提问作者Marc Wittke
相关产品推荐
相关产品推荐

