单元测试应Mock方法还是整个服务?API三层架构测试困惑
我的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方法的测试有意义吗?
当然有意义。你的服务层方法做了两件关键事情:
- 调用仓储层的
Get方法时,传入了正确的参数(这里是null和转换后的order); - 将仓储返回的
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

