C#同一类多实例依赖注入:如何实现单元测试可Mock?
如何通过依赖注入重构代码以支持单元测试Mock?
完全可以,而且这是解耦代码、支持单元测试的标准做法。你当前的实现直接在MyClass构造函数里实例化ServicebusService,导致两类紧耦合,无法单独Mock依赖。通过依赖注入重构后,就能轻松对ServicebusService进行Mock,完成单元测试。
第一步:定义接口解耦
首先给ServicebusService抽象出一个接口,把所有需要被调用的公共方法都放到接口中:
public interface IServicebusService { // 示例方法,替换成你实际需要的公共方法 Task SendMessageAsync(string message); } public class ServicebusService : IServicebusService { public ServicebusService(ServiceBusClient serviceBusClient, string queueName) { // 原构造逻辑保留 } // 实现接口中的方法 public async Task SendMessageAsync(string message) { // 原方法逻辑保留 } }
第二步:选择依赖注入方式
接下来有两种常见的方式实现注入,根据你的DI容器支持情况选择:
方式一:键控/命名服务(适合ASP.NET Core 8+或第三方DI容器)
如果用的是ASP.NET Core 8及以上版本,或者Autofac这类支持命名服务的容器,可以直接注册两个不同队列的IServicebusService实例,用键名区分:
// 在Program.cs/Startup.cs的DI配置中 builder.Services.AddKeyedSingleton<IServicebusService>("queue1", (sp, key) => new ServicebusService(sp.GetRequiredService<ServiceBusClient>(), "queue1")); builder.Services.AddKeyedSingleton<IServicebusService>("queue2", (sp, key) => new ServicebusService(sp.GetRequiredService<ServiceBusClient>(), "queue2"));
然后修改MyClass的构造函数,通过[FromKeyedServices]特性注入指定实例:
public class MyClass { private readonly IServicebusService _sbs1; private readonly IServicebusService _sbs2; public MyClass([FromKeyedServices("queue1")] IServicebusService sbs1, [FromKeyedServices("queue2")] IServicebusService sbs2) { _sbs1 = sbs1; _sbs2 = sbs2; } // MyClass的其他业务逻辑 }
方式二:工厂模式(兼容所有DI容器)
如果你的DI容器不支持键控/命名服务,用工厂模式更通用:
先定义工厂接口和实现:
public interface IServicebusServiceFactory { IServicebusService Create(string queueName); } public class ServicebusServiceFactory : IServicebusServiceFactory { private readonly ServiceBusClient _serviceBusClient; public ServicebusServiceFactory(ServiceBusClient serviceBusClient) { _serviceBusClient = serviceBusClient; } public IServicebusService Create(string queueName) { return new ServicebusService(_serviceBusClient, queueName); } }
注册工厂到DI容器:
builder.Services.AddSingleton<IServicebusServiceFactory, ServicebusServiceFactory>();
修改MyClass的构造函数,注入工厂后创建实例:
public class MyClass { private readonly IServicebusService _sbs1; private readonly IServicebusService _sbs2; public MyClass(IServicebusServiceFactory factory) { _sbs1 = factory.Create("queue1"); _sbs2 = factory.Create("queue2"); } // MyClass的其他业务逻辑 }
单元测试中的Mock示例
用Moq框架为例,不管哪种注入方式,都能轻松Mock依赖:
针对键控服务的测试
[TestClass] public class MyClassTests { [TestMethod] public async Task MyBusinessMethod_ExecutesCorrectly() { // Mock两个IServicebusService实例 var mockSbs1 = new Mock<IServicebusService>(); var mockSbs2 = new Mock<IServicebusService>(); // 配置Mock行为:比如调用SendMessageAsync时返回成功 mockSbs1.Setup(x => x.SendMessageAsync(It.IsAny<string>())).Returns(Task.CompletedTask); // 实例化MyClass,传入Mock实例 var myClass = new MyClass(mockSbs1.Object, mockSbs2.Object); // 执行要测试的方法 await myClass.MyBusinessMethod(); // 验证Mock方法是否被正确调用 mockSbs1.Verify(x => x.SendMessageAsync(It.IsAny<string>()), Times.Once); } }
针对工厂模式的测试
[TestMethod] public async Task MyBusinessMethod_ExecutesCorrectly() { var mockSbs1 = new Mock<IServicebusService>(); var mockSbs2 = new Mock<IServicebusService>(); // Mock工厂,指定不同队列返回对应的Mock实例 var mockFactory = new Mock<IServicebusServiceFactory>(); mockFactory.Setup(f => f.Create("queue1")).Returns(mockSbs1.Object); mockFactory.Setup(f => f.Create("queue2")).Returns(mockSbs2.Object); var myClass = new MyClass(mockFactory.Object); await myClass.MyBusinessMethod(); mockSbs1.Verify(x => x.SendMessageAsync(It.IsAny<string>()), Times.Once); }
内容的提问来源于stack exchange,提问作者Tommy Selggren
相关产品推荐
相关产品推荐

