集成测试中如何Mock MongoDB.Driver实现支持Filter/UpdateDefinition的内存存储?
解决方案:Mock MongoDB.Driver实现内存存储并支持Filter/UpdateDefinition
方案1:手动封装内存集合(利用MongoDB内置API处理Filter/Update)
无需第三方库,直接基于MongoDB.Driver的内置能力实现内存版IMongoCollection<TDocument>,自动处理复杂的Filter和Update逻辑:
核心思路:
- 用
FilterDefinition.ToExpression()(需引用MongoDB.Driver.Linq)将Filter转换为LINQ表达式,直接过滤内存集合 - 用
UpdateDefinition.Render()生成更新指令,通过BsonDocument序列化/反序列化应用更新到POCO对象
示例实现核心代码:
using MongoDB.Driver; using MongoDB.Driver.Linq; using System.Collections.Generic; using System.Linq; public class InMemoryMongoCollection<TDocument> : IMongoCollection<TDocument> { private readonly List<TDocument> _documents = new(); private readonly MongoCollectionSettings _settings; public InMemoryMongoCollection(MongoCollectionSettings settings = null) { _settings = settings ?? new MongoCollectionSettings(); } // 核心Find操作:自动转换Filter为LINQ表达式 public IFindFluent<TDocument, TDocument> Find(FilterDefinition<TDocument> filter, FindOptions options = null) { var filterExpr = filter.ToExpression(); var query = _documents.AsQueryable().Where(filterExpr); return new InMemoryFindFluent<TDocument, TDocument>(query); } // 核心Update操作:利用MongoDB内置更新逻辑处理UpdateDefinition public UpdateResult UpdateMany(FilterDefinition<TDocument> filter, UpdateDefinition<TDocument> update, UpdateOptions options = null) { var filterExpr = filter.ToExpression().Compile(); var targetDocs = _documents.Where(filterExpr).ToList(); var serializer = BsonSerializer.SerializerRegistry.GetSerializer<TDocument>(); var renderedUpdate = update.Render(serializer, BsonSerializer.SerializerRegistry); foreach (var doc in targetDocs) { var docBson = doc.ToBsonDocument(); renderedUpdate.ApplyTo(docBson); var updatedDoc = BsonSerializer.Deserialize<TDocument>(docBson); var index = _documents.IndexOf(doc); _documents[index] = updatedDoc; } return UpdateResult.ModifiedCount(targetDocs.Count); } // 基础Insert操作 public void InsertOne(TDocument document, InsertOneOptions options = null) { _documents.Add(document); } // 按需实现其他IMongoCollection成员(如Delete、Count等,逻辑类似) public string Name => _settings.CollectionName; public IMongoDatabase Database => throw new System.NotImplementedException(); public MongoCollectionSettings Settings => _settings; } // 简化版IFindFluent实现 public class InMemoryFindFluent<TInput, TOutput> : IFindFluent<TInput, TOutput> { private readonly IQueryable<TInput> _query; public InMemoryFindFluent(IQueryable<TInput> query) { _query = query; } public List<TOutput> ToList() { return typeof(TOutput) == typeof(TInput) ? _query.Cast<TOutput>().ToList() : throw new System.NotImplementedException("投影功能需额外实现"); } public TOutput FirstOrDefault() { return _query.Cast<TOutput>().FirstOrDefault(); } // 按需实现Sort、Project等其他IFindFluent成员 public IFindFluent<TInput, TOutput> Sort(SortDefinition<TInput> sort) { throw new System.NotImplementedException(); } }
方案2:使用MongoDB.Entities的内存模式
第三方库MongoDB.Entities内置了完整的内存存储实现,无需手动封装,直接支持所有MongoDB操作语法:
// 初始化内存数据库 await DB.InitAsync("TestDb", isInMemory: true); // 正常使用MongoDB语法操作 var book = new Book { Title = "C# in Depth" }; await book.SaveAsync(); // 使用Lambda或FilterDefinition查询 var filter = DB.Filter<Book>().Where(b => b.Title.Contains("C#")); var result = await DB.FindAsync(filter); // 执行Update操作 var update = DB.Update<Book>().Set(b => b.Title, "C# in Depth (Updated)"); await DB.UpdateAsync(filter, update);
方案3:优化Mongo2Go的使用(减少耗时)
如果必须依赖真实MongoDB逻辑,通过复用数据库实例而非每次测试重启来优化耗时:
private static MongoDbRunner _runner; private static IMongoDatabase _database; [ClassInitialize] public static void ClassInitialize(TestContext context) { // 仅在测试类启动时初始化一次数据库 _runner = MongoDbRunner.Start(); var client = new MongoClient(_runner.ConnectionString); _database = client.GetDatabase("TestDb"); } [ClassCleanup] public static void ClassCleanup() { _runner.Dispose(); } [TestInitialize] public void TestInitialize() { // 每次测试前仅清空集合,不重启数据库 foreach (var collName in _database.ListCollectionNames().ToList()) { _database.DropCollection(collName); } }
内容的提问来源于stack exchange,提问作者Ved Singhal
相关产品推荐
相关产品推荐

