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

如何Mock被测系统(SUT)中调用的构造函数?

解决方案:通过抽取实体创建逻辑实现隔离测试

重构思路

核心问题是当前服务直接new DataEntity(dto),导致测试时必须依赖合法的DTO实例。解决办法是把DataEntity的创建逻辑抽成独立的依赖,让服务通过接口调用创建逻辑,而非直接实例化。

步骤1:定义实体工厂接口

新增一个专门负责从DTO生成DataEntity的接口,把构造逻辑移到这个接口的实现中:

public interface IDataEntityFactory
{
    DataEntity Create(Dto dto);
}

步骤2:实现工厂类

这个类只负责封装DataEntity的构造逻辑,因为构造函数已经有独立测试,这里不需要额外逻辑:

public class DataEntityFactory : IDataEntityFactory
{
    public DataEntity Create(Dto dto)
    {
        return new DataEntity(dto);
    }
}

步骤3:修改原服务代码

在服务中注入IDataEntityFactory,替换掉直接new的逻辑:

// 服务构造函数新增工厂注入
private readonly IInjectedDtoBuilder _injectedDtoBuilder;
private readonly IInjectedRepository _injectedRepository;
private readonly IDataEntityFactory _entityFactory;

public YourService(IInjectedDtoBuilder injectedDtoBuilder, 
                   IInjectedRepository injectedRepository,
                   IDataEntityFactory entityFactory)
{
    _injectedDtoBuilder = injectedDtoBuilder;
    _injectedRepository = injectedRepository;
    _entityFactory = entityFactory;
}

public async Task ExecuteAsync()
{
    IEnumerable<Dto> dtos = await _injectedDtoBuilder.CreateAsync(_injectedParameters);

    foreach (Dto dto in dtos)
    {
        // 用工厂创建实体,而非直接new
        DataEntity dataEntity = _entityFactory.Create(dto);

        await _injectedRepository.AddAsync(dataEntity);
    }
}

单元测试方案

现在可以完全隔离DataEntity的构造逻辑,通过Moq Mock工厂,返回预设的实体实例,无需构造完整DTO:

[Test]
public async Task ExecuteAsync_ProcessesAllDtos_AndCallsRepositoryAdd()
{
    // 准备测试数据:随便造几个DTO,不用填充完整字段
    var testDtos = new List<Dto> { new Dto(), new Dto() };
    var testEntities = testDtos.Select(_ => new DataEntity(new Dto())).ToList();

    // Mock各依赖
    var mockDtoBuilder = new Mock<IInjectedDtoBuilder>();
    mockDtoBuilder.Setup(b => b.CreateAsync(It.IsAny<YourParameterType>()))
                  .ReturnsAsync(testDtos);

    var mockEntityFactory = new Mock<IDataEntityFactory>();
    for (int i = 0; i < testDtos.Count; i++)
    {
        mockEntityFactory.Setup(f => f.Create(testDtos[i]))
                         .Returns(testEntities[i]);
    }

    var mockRepository = new Mock<IInjectedRepository>();

    // 实例化服务
    var service = new YourService(mockDtoBuilder.Object, 
                                  mockRepository.Object, 
                                  mockEntityFactory.Object);

    // 执行测试方法
    await service.ExecuteAsync();

    // 验证每个实体都被正确传入Repository的AddAsync
    foreach (var entity in testEntities)
    {
        mockRepository.Verify(r => r.AddAsync(entity, It.IsAny<CancellationToken>()), 
                              Times.Once, 
                              "每个实体都应该被调用一次AddAsync");
    }
}

可选简化方案:用委托替代接口

如果觉得新增接口太繁琐,也可以用Func<Dto, DataEntity>作为依赖注入到服务中,代码更轻量:

// 服务构造函数改为注入委托
public YourService(IInjectedDtoBuilder injectedDtoBuilder, 
                   IInjectedRepository injectedRepository,
                   Func<Dto, DataEntity> entityCreator)
{
    _injectedDtoBuilder = injectedDtoBuilder;
    _injectedRepository = injectedRepository;
    _entityCreator = entityCreator;
}

// 执行方法中调用委托创建实体
DataEntity dataEntity = _entityCreator(dto);

测试时直接传入一个返回预设实体的委托即可,不需要Mock接口:

var testEntity = new DataEntity(new Dto());
Func<Dto, DataEntity> mockCreator = _ => testEntity;

内容的提问来源于stack exchange,提问作者Tam Coton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 18:42:48