You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:33:14