.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
相关产品推荐
相关产品推荐

