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

Moq模拟IMongoCollection时Database为空的单元测试问题求助

问题:单元测试中访问IMongoCollection.Database返回null

在单元测试调用SaveEmployee方法时,发现collection.Database返回null,无法继续执行后续的Client.StartSessionAsync操作。相关测试代码如下:

[Fact] 
public async void SaveEmployee_Should_Upsert_The_Employee()
{
    var mockDbService = new Mock<IDatabaseService>();
    var employeeMockCollection = new Mock<IMongoCollection<Employee>>();

    mockDbService
        .Setup(service => service.GetCollection("Employee"))
        .Returns(employeeMockCollection.Object);
    
    // 原代码中collectionMock应为employeeMockCollection,此处修正笔误
    employeeMockCollection
        .Setup(
            collection => collection.UpdateOneAsync(
                It.IsAny<FilterDefinition<Employee>>(),
                It.IsAny<UpdateDefinition<Employee>>(),
                It.IsAny<UpdateOptions>(),
                CancellationToken.None
            )
        )
        .Returns(Task.FromResult(new UpdateResult.Acknowledged(1, 1, null)));

    await SaveEmployee(employeeMockCollection.Object);
}

public async Task SaveEmployee(IMongoCollection<Employee> collection)
{
    var session = await collection
        .Database
        .Client
        .StartSessionAsync(new ClientSessionOptions());
    // collection.Database 此处为null
}
解决方案

Moq创建的Mock对象,所有未手动配置的属性默认返回null。你需要逐层Mock依赖链,依次配置IMongoCollection的Database属性、IMongoDatabase的Client属性,才能让链式调用不返回null。

修改后的测试代码如下:

[Fact] 
public async void SaveEmployee_Should_Upsert_The_Employee()
{
    // 1. Mock IMongoClient,并配置StartSessionAsync返回Mock会话
    var mockClient = new Mock<IMongoClient>();
    mockClient.Setup(c => c.StartSessionAsync(It.IsAny<ClientSessionOptions>(), It.IsAny<CancellationToken>()))
              .ReturnsAsync(Mock.Of<IClientSessionHandle>());

    // 2. Mock IMongoDatabase,配置其Client属性返回上面的MockClient
    var mockDatabase = new Mock<IMongoDatabase>();
    mockDatabase.Setup(db => db.Client).Returns(mockClient.Object);

    // 3. Mock IMongoCollection<Employee>,配置其Database属性返回上面的MockDatabase
    var employeeMockCollection = new Mock<IMongoCollection<Employee>>();
    employeeMockCollection.Setup(col => col.Database).Returns(mockDatabase.Object);

    // 保留原有的UpdateOneAsync配置
    employeeMockCollection
        .Setup(
            collection => collection.UpdateOneAsync(
                It.IsAny<FilterDefinition<Employee>>(),
                It.IsAny<UpdateDefinition<Employee>>(),
                It.IsAny<UpdateOptions>(),
                CancellationToken.None
            )
        )
        .Returns(Task.FromResult(new UpdateResult.Acknowledged(1, 1, null)));

    var mockDbService = new Mock<IDatabaseService>();
    mockDbService
        .Setup(service => service.GetCollection("Employee"))
        .Returns(employeeMockCollection.Object);

    await SaveEmployee(employeeMockCollection.Object);
}
关键说明
  • 必须逐层Mock依赖链:IMongoCollection → IMongoDatabase → IMongoClient,每个层级的属性都要手动配置返回对应的Mock对象。
  • 原代码中SaveEmployee方法是同步方法,但内部调用了异步的StartSessionAsync,需改为async Task方法并添加await,避免异步操作未等待的潜在问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 07:35:18