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

如何用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 { }

关键说明

  1. Mock依赖而非实现类:我们没有MockMongoDBRepository本身,而是Mock它的内部依赖IMongoCollection和ILogger,这样测试的是真实的GetData逻辑。
  2. 处理Expression参数:直接传入正常的Expression<Func<TEntity, bool>>即可,GetData内部会处理编译和过滤,我们只需要验证返回结果是否符合预期。
  3. 异常场景验证:通过Mock让AsQueryable抛出异常,验证方法是否返回空列表并正确调用日志。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:43:27