如何在.NET Core中为MongoDB的IMongoCollection分页扩展方法编写单元测试
单元测试实现方案
你可以根据场景选择两种测试方案:
方案1:基于Moq的纯单元测试(无需真实MongoDB实例)
依赖安装
先安装对应NuGet包:
xunit(测试框架,也可替换为NUnit)Moq(依赖Mock框架)MongoDB.Driver(MongoDB官方驱动)xunit.runner.visualstudio(Visual Studio运行测试依赖)
测试代码实现
首先定义测试用的实体类:
public class TestEntity : class { public ObjectId Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
然后编写测试类,模拟IMongoCollection的调用链路,验证分页逻辑正确性:
using Moq; using Xunit; using MongoDB.Driver; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; public class DataPagerExtensionTests { private readonly Mock<IMongoCollection<TestEntity>> _mockCollection; private readonly List<TestEntity> _testData; public DataPagerExtensionTests() { _mockCollection = new Mock<IMongoCollection<TestEntity>>(); // 构造10条测试数据 _testData = Enumerable.Range(1, 10).Select(i => new TestEntity { Id = ObjectId.GenerateNewId(), Name = $"测试{i}", Age = 20 + i }).ToList(); } [Fact] public async Task Paginate_ShouldReturnCorrectPagedData_WhenParametersValid() { // Arrange var page = 2; var pageSize = 3; var expectedTotalItems = 10; var expectedTotalPages = 4; var expectedItems = _testData.Skip(3).Take(3).ToList(); // Mock CountDocuments计数方法 var mockCountFluent = new Mock<IFindFluent<TestEntity, TestEntity>>(); mockCountFluent.Setup(f => f.CountDocuments(CancellationToken.None)) .Returns(expectedTotalItems); // Mock 查询链路的Find、Sort、Skip、Limit、ToListAsync方法 var mockQueryFluent = new Mock<IFindFluent<TestEntity, TestEntity>>(); mockQueryFluent.Setup(f => f.Sort(It.IsAny<SortDefinition<TestEntity>>())) .Returns(mockQueryFluent.Object); mockQueryFluent.Setup(f => f.Skip(It.IsAny<int>())) .Returns(mockQueryFluent.Object); mockQueryFluent.Setup(f => f.Limit(It.IsAny<int>())) .Returns(mockQueryFluent.Object); mockQueryFluent.Setup(f => f.ToListAsync(CancellationToken.None)) .ReturnsAsync(expectedItems); // 模拟Collection的Find方法,第一次调用返回计数用Mock,第二次返回查询用Mock var callCount = 0; _mockCollection.Setup(c => c.Find(It.IsAny<FilterDefinition<TestEntity>>(), null)) .Returns(() => { callCount++; return callCount == 1 ? mockCountFluent.Object : mockQueryFluent.Object; }); // Act var result = await _mockCollection.Object.Paginate( FilterDefinition<TestEntity>.Empty, SortDefinition<TestEntity>.Empty, page, pageSize, true); // Assert Assert.Equal(page, result.CurrentPage); Assert.Equal(pageSize, result.PageSize); Assert.Equal(expectedTotalItems, result.TotalItems); Assert.Equal(expectedTotalPages, result.TotalPages); Assert.Equal(expectedItems.Count, result.Items.Count); Assert.Equal(expectedItems.First().Name, result.Items.First().Name); } }
方案2:基于内存MongoDB的集成测试(更贴近真实场景)
如果要避免Mock链路遗漏、更准确验证MongoDB驱动的实际行为,可以用Mongo2Go包启动临时内存MongoDB实例做测试:
依赖安装
额外安装NuGet包:
Mongo2Go
测试代码实现
using Mongo2Go; using Xunit; using MongoDB.Driver; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; public class DataPagerExtensionIntegrationTests : IAsyncLifetime { private MongoDbRunner _runner; private IMongoCollection<TestEntity> _collection; private readonly List<TestEntity> _testData = Enumerable.Range(1, 10).Select(i => new TestEntity { Id = ObjectId.GenerateNewId(), Name = $"测试{i}", Age = 20 + i }).ToList(); public async Task InitializeAsync() { // 启动内存MongoDB实例 _runner = MongoDbRunner.Start(); var client = new MongoClient(_runner.ConnectionString); var db = client.GetDatabase("TestDb"); _collection = db.GetCollection<TestEntity>("TestEntities"); // 预插入测试数据 await _collection.InsertManyAsync(_testData); } public async Task DisposeAsync() { _runner.Dispose(); } [Fact] public async Task Paginate_ShouldReturnCorrectPagedResult_WithRealMongoInstance() { // Arrange var page = 2; var pageSize = 3; var sort = Builders<TestEntity>.Sort.Ascending(x => x.Age); // Act var result = await _collection.Paginate( FilterDefinition<TestEntity>.Empty, sort, page, pageSize, true); // Assert Assert.Equal(2, result.CurrentPage); Assert.Equal(3, result.PageSize); Assert.Equal(10, result.TotalItems); Assert.Equal(4, result.TotalPages); Assert.Equal(3, result.Items.Count); Assert.Equal("测试4", result.Items.First().Name); Assert.Equal("测试6", result.Items.Last().Name); } [Fact] public async Task Paginate_ShouldReturnEmptyList_WhenPageExceedsTotalPages() { // Arrange var page = 10; var pageSize = 3; // Act var result = await _collection.Paginate( FilterDefinition<TestEntity>.Empty, Builders<TestEntity>.Sort.Ascending(x => x.Age), page, pageSize, true); // Assert Assert.Empty(result.Items); Assert.Equal(10, result.TotalItems); Assert.Equal(4, result.TotalPages); } }
注意:你当前的扩展方法里传入了
paginationRequired参数但没有使用,可以在测试时补充对应逻辑的校验,比如该参数为false时直接返回全量数据的场景。
内容的提问来源于stack exchange,提问作者rakesh mahur
相关产品推荐
相关产品推荐

