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

.NET Core中MongoDB仓库单元测试Mock对象异常问题

为MongoDB交互的Repository编写单元测试时的Mock异常问题

我尝试为与MongoDB交互的Repository编写单元测试,但在Mock对象时持续遇到异常。以下是我的代码实现和测试代码:

Repository实现代码

public class UserManagementRepository : IUserManagementRepository
{
    private readonly IMongoCollection<UserModel> _users;
    public UserManagementRepository(IDatabaseSettings dbSettings, IApplicationSettings applicationSettings, IMongoClient mongoClient)
    {
        IMongoDatabase database = mongoClient.GetDatabase(dbSettings.DatabaseName);
        _users = database.GetCollection<UserModel>(applicationSettings.UserCollectionName);
    }
    
    public async Task<GeneralResponse> Get(string id)
    {
        GeneralResponse response = new GeneralResponse();
        try
        {
            IAsyncCursor<UserModel> userModel = await _users.FindAsync(user => user.Id == id);
            if (userModel != null)
            {
                response.Message = "User exists!";
                response.Data = userModel.FirstOrDefault();
                response.ResponseCode = ResponseCode.Success;
            }
            else
            {
                response.Message = $"User with Id: {id} not found!";
                response.ResponseCode = ResponseCode.Success;
            }
        }
        catch (Exception ex)
        {
            response.Message = "Failure";
            response.ResponseCode = ResponseCode.Error;
        }
        return response;
    }
}

测试类代码

public class UserManagmentRepositoryTests
{
    private Mock<IDatabaseSettings> _mockDbSettings;
    private Mock<IApplicationSettings> _mockApplicationSettings;
    private Mock<IMongoClient> _mockClient;
    public UserManagmentRepositoryTests()
    {
        _mockDbSettings = new Mock<IDatabaseSettings>();
        _mockApplicationSettings = new Mock<IApplicationSettings>();
        _mockClient = new Mock<IMongoClient>();
    }

    [Fact]
    public async Task GetUserWithId_Test()
    {
        // Arrange
        var repo = new Mock<IUserManagementRepository>();
        IDatabaseSettings dbSettings = new DatabaseSettings()
        {
            ConnectionString = "mongodb:connectionstring",
            DatabaseName = "dbname"
        };
        _mockDbSettings.Setup(x => x).Returns(dbSettings);

        IApplicationSettings applicationSettings = new ApplicationSettings() { UserCollectionName = "users" };
        _mockApplicationSettings.Setup(app => applicationSettings).Returns(applicationSettings);

        MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(dbSettings.ConnectionString);
        MongoClient client = new MongoClient(clientSettings);
        _mockClient.Setup(c => client);

        var ctr = new UserManagementRepository(_mockDbSettings.Object, _mockApplicationSettings.Object, _mockClient.Object);

        // Act
        var result = ctr.Get("132");

        // Assert
        //result.StatusCode.Should().NotBeNull();
    }
}

异常情况

每次执行Setup操作时都会抛出异常,异常截图如下:
异常截图


问题分析与解决方案

你的测试代码存在多处Moq使用错误,导致异常:

1. Moq Setup语法错误

你直接对整个Mock对象进行Setup(比如_mockDbSettings.Setup(x => x).Returns(dbSettings);),这不符合Moq的使用规范。Moq需要针对接口的属性或方法进行Setup,而非整个对象。

2. 未完整Mock MongoDB依赖链

Repository的构造函数依赖IMongoClient,而IMongoClient需要返回IMongoDatabase,IMongoDatabase又要返回IMongoCollection<UserModel>,你没有完整Mock这条依赖链,导致Repository初始化时无法获取到_users集合。

修正后的测试代码

public class UserManagmentRepositoryTests
{
    private Mock<IDatabaseSettings> _mockDbSettings;
    private Mock<IApplicationSettings> _mockApplicationSettings;
    private Mock<IMongoClient> _mockClient;
    private Mock<IMongoDatabase> _mockDatabase;
    private Mock<IMongoCollection<UserModel>> _mockUsersCollection;

    public UserManagmentRepositoryTests()
    {
        _mockDbSettings = new Mock<IDatabaseSettings>();
        _mockApplicationSettings = new Mock<IApplicationSettings>();
        _mockClient = new Mock<IMongoClient>();
        _mockDatabase = new Mock<IMongoDatabase>();
        _mockUsersCollection = new Mock<IMongoCollection<UserModel>>();
    }

    [Fact]
    public async Task Get_ExistingUserId_ReturnsSuccessWithUser()
    {
        // Arrange
        // Setup配置类属性
        _mockDbSettings.Setup(x => x.DatabaseName).Returns("dbname");
        _mockApplicationSettings.Setup(x => x.UserCollectionName).Returns("users");

        // Mock MongoClient返回Mock的Database
        _mockClient.Setup(client => client.GetDatabase(It.IsAny<string>(), null))
                   .Returns(_mockDatabase.Object);

        // Mock Database返回Mock的Collection
        _mockDatabase.Setup(db => db.GetCollection<UserModel>(It.IsAny<string>(), null))
                     .Returns(_mockUsersCollection.Object);

        // 构造测试用的用户数据
        var testUser = new UserModel { Id = "132", Name = "Test User" };
        // Mock FindAsync返回包含测试用户的IAsyncCursor
        var mockCursor = new Mock<IAsyncCursor<UserModel>>();
        mockCursor.SetupSequence(cursor => cursor.MoveNextAsync(It.IsAny<CancellationToken>()))
                  .ReturnsAsync(true)
                  .ReturnsAsync(false);
        mockCursor.Setup(cursor => cursor.Current).Returns(new List<UserModel> { testUser });

        _mockUsersCollection.Setup(col => col.FindAsync(
            It.IsAny<FilterDefinition<UserModel>>(),
            It.IsAny<FindOptions<UserModel, UserModel>>(),
            It.IsAny<CancellationToken>()))
        .ReturnsAsync(mockCursor.Object);

        // 实例化真实的Repository(不是Mock)
        var repository = new UserManagementRepository(
            _mockDbSettings.Object, 
            _mockApplicationSettings.Object, 
            _mockClient.Object);

        // Act
        var result = await repository.Get("132");

        // Assert
        result.ResponseCode.Should().Be(ResponseCode.Success);
        result.Message.Should().Be("User exists!");
        var returnedUser = result.Data as UserModel;
        returnedUser.Should().NotBeNull();
        returnedUser.Id.Should().Be("132");
    }

    [Fact]
    public async Task Get_NonExistingUserId_ReturnsSuccessWithNotFoundMessage()
    {
        // Arrange
        _mockDbSettings.Setup(x => x.DatabaseName).Returns("dbname");
        _mockApplicationSettings.Setup(x => x.UserCollectionName).Returns("users");

        _mockClient.Setup(client => client.GetDatabase(It.IsAny<string>(), null))
                   .Returns(_mockDatabase.Object);

        _mockDatabase.Setup(db => db.GetCollection<UserModel>(It.IsAny<string>(), null))
                     .Returns(_mockUsersCollection.Object);

        // Mock空的IAsyncCursor
        var mockCursor = new Mock<IAsyncCursor<UserModel>>();
        mockCursor.SetupSequence(cursor => cursor.MoveNextAsync(It.IsAny<CancellationToken>()))
                  .ReturnsAsync(false);
        mockCursor.Setup(cursor => cursor.Current).Returns(new List<UserModel>());

        _mockUsersCollection.Setup(col => col.FindAsync(
            It.IsAny<FilterDefinition<UserModel>>(),
            It.IsAny<FindOptions<UserModel, UserModel>>(),
            It.IsAny<CancellationToken>()))
        .ReturnsAsync(mockCursor.Object);

        var repository = new UserManagementRepository(
            _mockDbSettings.Object, 
            _mockApplicationSettings.Object, 
            _mockClient.Object);

        // Act
        var result = await repository.Get("999");

        // Assert
        result.ResponseCode.Should().Be(ResponseCode.Success);
        result.Message.Should().Be("User with Id: 999 not found!");
        result.Data.Should().BeNull();
    }
}

关键修正点说明

  • 针对接口的具体属性进行Setup,比如_mockDbSettings.Setup(x => x.DatabaseName).Returns("dbname");
  • 完整Mock MongoDB的依赖链:IMongoClient → IMongoDatabase → IMongoCollection<UserModel>
  • 模拟FindAsync返回的IAsyncCursor,通过SetupSequence控制MoveNextAsync的返回值,模拟有数据/无数据的场景
  • 测试方法标记为async,并使用await调用Repository的异步方法Get
  • 实例化真实的Repository对象进行测试,而非Mock Repository接口(单元测试需要验证真实的实现逻辑)

内容的提问来源于stack exchange,提问作者J.SMTBCJ15

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 13:30:52