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

单元测试应Mock方法还是整个服务?API三层架构测试困惑

问题:服务层单元测试的Mock选择

我的API采用三层架构:控制器层(controllers)、服务层(services)、仓储层(repository)。我想对服务层的这个简化方法进行单元测试:

public async IAsyncEnumerable<TEntityDTO> GetAllEntities(SortingOrder orderBy)
{
    var allItems = _unitOfWork.Entity.Get(null, order);
    await foreach (TEntity item in allItems)
    {
        yield return _mapper.Map<TEntityDTO>(item);
    }
}

其中_unitOfWork是注入的UnitOfWork类实例,orderBy参数会转换为order并传入仓储层的Get方法。

我有两个Mock选项:

  • Mock _unitOfWork
  • Mock _unitOfWork.Entity.Get(null, order)

第一种情况会使用仓储层真实的Get方法,若测试失败,问题可能出在服务层或仓储层,这可能不再是严格的单元测试。
第二种情况,单元测试仅验证foreach循环,这样的测试是否有意义?
我到底应该Mock哪个?

更新:单元测试代码

public async Task GetAllEntities_FetchEntities_ReturnsCorrectNumberOfEntities<TEntity, TEntityDTO>()
    where TEntity : class, IEntityBase, new()
    where TEntityDTO : class
{
    //Arrange
    var unitOfWork=new Mock<IUnitOfWork<TEntity>>();
    unitOfWork.Setup(u => u.Entity.Get(null, null)).Returns(this.GetAsyncEntities<TEntity>);
    ServiceBase<TEntityDTO, TEntity> service = new ServiceBase<TEntityDTO, TEntity>(unitOfWork.Object, Mapper);
   //Act
    List<TEntityDTO> entities = new List<TEntityDTO>();
    await foreach (var item in service.GetAllEntities(orderBy:SortingOrder.None))
    {
        entities.Add(item);
    }
    //Assert
    Assert.Equal(3, entities.Count);
}

回答

你应该Mock整个_unitOfWork的依赖接口(也就是你的IUnitOfWork<TEntity>),并指定其Entity.Get方法的返回值——这才是严格单元测试的正确做法。

为什么不能用真实仓储层的Get方法?

单元测试的核心是隔离被测单元(这里是服务层的GetAllEntities方法),只验证它自身的逻辑是否正确。如果使用真实的仓储层方法,一旦测试失败,你无法确定是服务层的逻辑(比如DTO映射、异步遍历)出了问题,还是仓储层的Get方法本身有bug,这样的测试失去了单元测试的定位意义,变成了集成测试。

MockGet方法的测试有意义吗?

当然有意义。你的服务层方法做了两件关键事情:

  1. 调用仓储层的Get方法时,传入了正确的参数(这里是null和转换后的order);
  2. 将仓储返回的TEntity逐个映射为TEntityDTO,并以IAsyncEnumerable<TEntityDTO>的形式返回。

你的现有测试只验证了返回数量,但还可以补充更多断言:

  • 验证_unitOfWork.Entity.Get是否被调用,且传入的参数是否正确(比如当orderBy不是None时,对应的order参数是否正确传递);
  • 验证每个返回的TEntityDTO是否正确映射了TEntity的属性(比如可以在GetAsyncEntities中构造带有特定属性值的实体,然后断言DTO的对应属性匹配)。

优化你的现有测试

比如可以补充验证方法调用的断言:

// 在Assert部分添加
unitOfWork.Verify(u => u.Entity.Get(null, It.IsAny<YourOrderType>()), Times.Once);

这样能确保服务层正确调用了仓储的方法,而不仅仅是返回数量正确。

总结:严格的单元测试必须隔离所有外部依赖,所以MockIUnitOfWork<TEntity>并指定其Get方法的返回值是正确选择,这样的测试能精准验证服务层自身的逻辑是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:49:58