如何用C#与Moq框架为带Func参数的方法编写单元测试
针对带Func参数的Moq单元测试及virtual方法的影响解答
一、关于GetData方法标记为virtual的影响
- 如果你要Mock MongoDBRepository类本身(比如测试依赖它的上层服务),
virtual是必须的——因为Moq通过生成代理类覆盖目标方法,只有virtual/抽象方法才能被Moq拦截修改行为。 - 如果你要直接测试MongoDBRepository的逻辑(像下面的示例,Mock它的依赖而非Mock它自己),
virtual完全不影响,你可以正常实例化类并调用方法。
二、编写带Expression<Func<TEntity, bool>>参数的单元测试
以测试MongoDBRepository的GetData方法为例,我们需要Mock它的依赖(IMongoCollection<TEntity>和ILogger),验证不同场景下的返回结果和行为:
测试示例(使用xUnit + Moq)
public class MongoDBRepositoryTests { private readonly Mock<IMongoCollection<TestEntity>> _mockCollection; private readonly Mock<ILogger<MongoDBRepository<TestEntity>>> _mockLogger; private readonly MongoDBRepository<TestEntity> _repository; // 预设测试数据 private readonly List<TestEntity> _testData = new() { new TestEntity { Id = 1, Name = "Test1" }, new TestEntity { Id = 2, Name = "Test2" } }; public MongoDBRepositoryTests() { _mockCollection = new Mock<IMongoCollection<TestEntity>>(); _mockLogger = new Mock<ILogger<MongoDBRepository<TestEntity>>>(); // 配置IMongoCollection的AsQueryable方法返回预设数据 var queryableData = _testData.AsQueryable(); _mockCollection.Setup(c => c.AsQueryable<TestEntity>()).Returns(queryableData); // 实例化待测试的Repository _repository = new MongoDBRepository<TestEntity>(_mockCollection.Object, _mockLogger.Object); } // 测试无filter时返回全部数据 [Fact] public void GetData_WithoutFilter_ReturnsAllItems() { // 执行方法 var result = _repository.GetData(); // 断言结果 Assert.Equal(_testData.Count, result.Count()); Assert.Equal(_testData[0].Id, result.First().Id); } // 测试带filter时返回匹配数据 [Fact] public void GetData_WithFilter_ReturnsMatchingItems() { // 定义测试用的filter Expression<Func<TestEntity, bool>> filter = entity => entity.Id == 1; // 执行方法 var result = _repository.GetData(filter); // 断言结果 var matchedItems = result.ToList(); Assert.Single(matchedItems); Assert.Equal(1, matchedItems[0].Id); } // 测试集合抛出异常时返回空列表并记录错误日志 [Fact] public void GetData_WhenCollectionThrows_ReturnsEmptyAndLogsError() { // 配置Mock抛出异常 _mockCollection.Setup(c => c.AsQueryable<TestEntity>()).Throws(new Exception("Test error")); // 执行方法 var result = _repository.GetData(); // 断言结果和日志行为 Assert.Empty(result); _mockLogger.Verify(logger => logger.LogError( It.IsAny<Exception>(), "Query Failed"), Times.Once); } } // 测试用实体类(继承自EntityBase) public class TestEntity : EntityBase { public int Id { get; set; } public string Name { get; set; } } public abstract class EntityBase { }
关键说明
- Mock依赖而非实现类:我们没有Mock
MongoDBRepository本身,而是Mock它的内部依赖IMongoCollection和ILogger,这样测试的是真实的GetData逻辑。 - 处理Expression参数:直接传入正常的
Expression<Func<TEntity, bool>>即可,GetData内部会处理编译和过滤,我们只需要验证返回结果是否符合预期。 - 异常场景验证:通过Mock让
AsQueryable抛出异常,验证方法是否返回空列表并正确调用日志。
内容的提问来源于stack exchange,提问作者AMIT SHELKE
相关产品推荐
相关产品推荐

