Mongo.Fakes.Server 0.8.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package Mongo.Fakes.Server --version 0.8.1
                    
NuGet\Install-Package Mongo.Fakes.Server -Version 0.8.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mongo.Fakes.Server" Version="0.8.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mongo.Fakes.Server" Version="0.8.1" />
                    
Directory.Packages.props
<PackageReference Include="Mongo.Fakes.Server" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mongo.Fakes.Server --version 0.8.1
                    
#r "nuget: Mongo.Fakes.Server, 0.8.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mongo.Fakes.Server@0.8.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mongo.Fakes.Server&version=0.8.1
                    
Install as a Cake Addin
#tool nuget:?package=Mongo.Fakes.Server&version=0.8.1
                    
Install as a Cake Tool

Mongo.Fakes

Wire-compatible MongoDB test doubles for the official MongoDB C# driver — no mongod process required.

Mongo.Fakes is two things sharing one filter engine:

  • Mongo.Fakes.Core — compiles MongoDB filter documents (BsonDocument) into Expression<Func<BsonDocument, bool>> predicates. Stays entirely in BSON-land (BsonValue comparisons, MongoDB type ordering, null-vs-missing semantics) instead of mapping to CLR types, so behavior matches real MongoDB.
  • Mongo.Fakes.Server — an in-process MongoDB wire-protocol (OP_MSG) mock server that serves fixture data to a real IMongoClient/IMongoCollection, for tests that need to exercise actual driver code paths without standing up MongoDB.

Mongo.Fakes.Server uses Mongo.Fakes.Core as its filter engine, so operator semantics are implemented once and shared by both the lightweight in-memory predicate mode and the wire-protocol double.

When to Use Mongo.Fakes

Aspect Mongo.Fakes EphemeralMongo Testcontainers
Speed ⚡ Very fast (in-process) 🐢 Slower (real binary) 🐢 Slower (Docker)
Setup 0 ms, no dependencies ~100 MB binary download Docker required
Compatibility 95% (queries, writes, aggregations) 100% (full MongoDB) 100% (full MongoDB)
Fixture Setup Easy (JSON/BSON files) Any method Any method
Best For Unit tests, CI speed, fixture validation Integration tests needing 100% parity Feature demos, complex scenarios
Test Database Reuse ✓ Snapshot real data as BSON ✓ Full compatibility ✓ Full compatibility

Choose Mongo.Fakes if: Your tests don't use unsupported operators, you want instant startup, and you're testing driver integration paths rather than advanced MongoDB features.

Choose EphemeralMongo/Testcontainers if: You need 100% MongoDB compatibility or are testing features like transactions, geospatial queries, or complex aggregations.

Status

Early scaffold — see docs/SPEC.md for the design specification and current scope.

Packages

Package Purpose
Mongo.Fakes.Core Filter compiler: BsonDocument filter → LINQ predicate
Mongo.Fakes.Server Wire-protocol test double server backed by fixture files

Usage

Loading Fixture Data

From Local JSON/BSON Files

Create a folder structure matching your database layout:

Fixtures/
  myapp/
    users.json
    products.json
  other_db/
    items.json

Each line in the JSON files is a BSON document:

{"_id": 1, "name": "Alice", "email": "alice@example.com"}
{"_id": 2, "name": "Bob", "email": "bob@example.com"}
From MongoDB Dump (mongodump)

Import real exported data using mongodump:

mongodump --uri "mongodb://prod-server/myapp" --out ./dump

This creates a structure like: dump/myapp/users.bson, dump/myapp/products.bson, etc.

Then load it in your tests:

var backend = new BsonFileBackend(Path.Combine(
    Directory.GetCurrentDirectory(), "dump"), loadFromMongoDump: true);

The server automatically handles both .json and .bson files, making it easy to snapshot real production data as test fixtures.

Integration Tests with xUnit

Use MongoFakeServer with xUnit's IAsyncLifetime to manage server lifecycle in tests:

using MongoDB.Driver;
using Xunit;

public class UserServiceTests : IAsyncLifetime
{
    private MongoFakeServer _server;
    private IMongoClient _client;

    public async Task InitializeAsync()
    {
        // Start the fake server on an auto-assigned port
        var backend = new BsonFileBackend(Path.Combine(
            Directory.GetCurrentDirectory(), "Fixtures"));
        _server = new MongoFakeServer(backend, port: 0);
        await _server.StartAsync();

        // Use the connection string just like EphemeralMongo
        _client = new MongoClient(_server.ConnectionString);
    }

    public async Task DisposeAsync()
    {
        _client?.Dispose();
        if (_server != null)
            await _server.DisposeAsync();
    }

    [Fact]
    public async Task CreateUser_Should_Insert_Document()
    {
        var db = _client.GetDatabase("myapp");
        var users = db.GetCollection<BsonDocument>("users");

        var newUser = new BsonDocument
        {
            { "name", "Alice" },
            { "email", "alice@example.com" },
            { "age", 30 }
        };

        await users.InsertOneAsync(newUser);

        var found = await users.Find(
            Builders<BsonDocument>.Filter.Eq("email", "alice@example.com")
        ).FirstOrDefaultAsync();

        Assert.NotNull(found);
        Assert.Equal("Alice", found["name"].AsString);
    }

    [Fact]
    public async Task QueryWithFilter_Should_Return_Matching_Documents()
    {
        var db = _client.GetDatabase("myapp");
        var products = db.GetCollection<BsonDocument>("products");

        await products.InsertManyAsync(new[]
        {
            new BsonDocument { { "name", "Widget" }, { "price", 9.99 }, { "category", "tools" } },
            new BsonDocument { { "name", "Gadget" }, { "price", 19.99 }, { "category", "electronics" } },
            new BsonDocument { { "name", "Tool" }, { "price", 14.99 }, { "category", "tools" } }
        });

        var filter = Builders<BsonDocument>.Filter.Eq("category", "tools");
        var results = await products.Find(filter).ToListAsync();

        Assert.Equal(2, results.Count);
        Assert.All(results, doc => Assert.Equal("tools", doc["category"].AsString));
    }
}

The MongoFakeServer provides:

  • ConnectionString property for connecting with MongoClient (no process management needed)
  • Port property (auto-assigned if you pass port: 0)
  • StartAsync() / DisposeAsync() for lifecycle management
  • Semantics compatible with EphemeralMongo's MongoRunner

GridFS Support

GridFS operations (file upload/download via MongoDB.Driver.GridFSBucket) are fully supported without any special configuration:

var db = _client.GetDatabase("myapp");
var bucket = new GridFSBucket(db);

// Upload a file
var fileContent = Encoding.UTF8.GetBytes("Hello, World!");
var fileId = await bucket.UploadFromBytesAsync("hello.txt", fileContent);

// Download the file
var downloaded = await bucket.DownloadAsBytesAsync(fileId);

// Also supports streaming operations
using (var uploadStream = await bucket.OpenUploadStreamAsync("data.bin"))
{
    await uploadStream.WriteAsync(largeData, 0, largeData.Length);
}

GridFS works transparently by using the existing insert, find, update, and delete command support — no bucket-specific code is needed. Files spanning multiple chunks are handled automatically.

Performance: Copy-on-Write (CoW) Fixture Isolation

For test suites with hundreds of test fixtures sharing the same baseline data, Mongo.Fakes implements per-document copy-on-write (CoW) to minimize memory overhead:

  • Baseline data (loaded from fixture files) is shared across all test fixtures
  • Per-fixture mutations are tracked separately — when a test modifies a document, only the changed version is copied to heap
  • Unmodified documents reference the original baseline (zero memory overhead)

This enables thousands of concurrent test fixtures to maintain isolation without the memory cost of cloning 100MB+ baselines per fixture.

Typical scenario:

  • Baseline: 100 MB shared across all fixtures
  • Test Class A: inserts 5 docs, updates 3 → ~50 KB mutations
  • Test Class B: deletes 2 docs → ~20 KB mutations
  • Total memory: 100 MB + 70 KB (instead of 200 MB for naive cloning)

For tests that don't mutate data, the footprint is the baseline size alone — perfect for read-heavy test suites.

How CoW Works: DocumentSnapshot

Internally, Mongo.Fakes wraps each document in a DocumentSnapshot that tracks baseline vs. mutated state:

internal sealed class DocumentSnapshot
{
    public BsonDocument Original { get; }      // Shared baseline
    public BsonDocument? Mutated { get; set; } // Test-specific copy
    
    public BsonDocument Current => Mutated ?? Original;
    public bool IsDirty => Mutated != null;
}

Lifecycle:

  1. Load — Document is loaded from fixture file into Original (shared)
  2. Read — Tests read via Current (points to Original)
  3. Mutate — When modified, Mutated is populated with a copy
  4. Track — Future reads use Mutated for this snapshot only; other fixtures still see Original
  5. Cleanup — When test fixture is torn down, only Mutated is discarded; Original persists for next test

Tracking Snapshots with Instance Names

For complex test fixtures with many documents, you can name snapshots to track their state during debugging:

// Create a named snapshot (useful for logging/debugging)
var userSnapshot = new DocumentSnapshot(userDoc, instanceName: "user_alice");

// Later, you can log the state
Console.WriteLine(userSnapshot.GetDebugInfo());
// Output: Snapshot[user_alice] (id=1, state=baseline)

// After mutation
userSnapshot.Mutated = new BsonDocument(userSnapshot.Original) { { "email", "new@example.com" } };
Console.WriteLine(userSnapshot.GetDebugInfo());
// Output: Snapshot[user_alice] (id=1, state=mutated)

Naming Patterns:

Pattern Example Use Case
Collection + ID users_42 Default for tracking by collection
Fixture + Collection + Seq test_fixture_users_1 Multiple fixtures, explicit ordering
Human-readable admin_user_alice Debugging & test reports
Fixture name + field order_123_status Tracking specific field changes

Example with a SnapshotRegistry:

var registry = new DocumentSnapshotRegistry();

// Register and track snapshots
registry.Register("user_alice", new DocumentSnapshot(userDoc, "user_alice"));
registry.Register("user_bob", new DocumentSnapshot(bobDoc, "user_bob"));
registry.Register("product_laptop", new DocumentSnapshot(productDoc, "product_laptop"));

// Modify one
var alice = registry.Get("user_alice");
alice.Mutated = new BsonDocument(alice.Original) { { "status", "active" } };

// Get diagnostic summary
Console.WriteLine(registry.GetSummary());
/* Output:
Snapshot[user_alice] (id=1, state=mutated)
Snapshot[user_bob] (id=2, state=baseline)
Snapshot[product_laptop] (id=101, state=baseline)
*/

// Query dirty (modified) snapshots only
var dirtySnapshots = registry.GetDirty();
Console.WriteLine($"Modified snapshots: {dirtySnapshots.Count()}");
// Output: Modified snapshots: 1

Memory Visualization:

Before any modifications:
┌──────────────────────────────┐
│   Shared Baseline (100 MB)   │ ← All 3 test fixtures reference this
│   - 50,000 user documents    │
│   - 5,000 product documents  │
│   - Memory: 100 MB           │
└──────────────────────────────┘

After Test A modifies 3 users:
┌──────────────────────────────┐
│   Shared Baseline (100 MB)   │ ← Tests B & C still use original
│   - 50,000 user documents    │
│   - 5,000 product documents  │
└──────────────────────────────┘
        ↓ (Test A mutations)
    ┌────────────────┐
    │ Test A Copy    │ ← Only 3 modified users copied
    │ (50 KB)        │
    └────────────────┘

Total memory: 100 MB + 50 KB (vs. 300 MB if all cloned)

Building

dotnet build
dotnet test

Targets net8.0 and net10.0.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.11.2 52 9/14/2026
0.11.1 48 9/13/2026
0.11.0 109 9/5/2026
0.10.0 126 8/29/2026
0.9.0 96 8/27/2026
0.8.1 107 8/27/2026
0.8.0 106 8/26/2026
0.7.7 102 8/26/2026
0.7.6 93 8/26/2026
0.7.5 102 8/26/2026
0.7.4 106 8/26/2026
0.7.3 110 8/25/2026
0.7.2 107 8/25/2026
0.7.1 106 8/25/2026
0.7.0 104 8/25/2026
0.6.0 116 8/25/2026
0.5.0 95 8/25/2026
0.4.0 99 8/25/2026
0.3.1 101 8/25/2026
0.1.0 111 8/24/2026