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

集成测试中如何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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:23:22