DEV Community

Cover image for NimbleMock: A Modern, Blazing-Fast Mocking Library for .NET (34x Faster Than Moq)
Diego Teles
Diego Teles

Posted on

NimbleMock: A Modern, Blazing-Fast Mocking Library for .NET (34x Faster Than Moq)

NimbleMock: A Modern, Blazing-Fast Mocking Library for .NET (34x Faster Than Moq)

Hey devs! If you've ever felt the pain of verbose mocking setups, runtime overhead slowing down your test suites, or struggling to mock static methods like DateTime.Now without ugly wrappers, you're not alone. Traditional libraries like Moq and NSubstitute are great, but in large-scale .NET projects (especially with async-heavy code and generic repositories), they can feel dated and slow.

That's why I built NimbleMock — a zero-allocation, source-generated mocking library designed for modern .NET testing in 2025 and beyond.

Why I Built NimbleMock

Over the years, I've dealt with common mocking frustrations in enterprise .NET apps:

  • Verbose setups for large interfaces (mocking 20+ methods just to test one).
  • Performance hits from runtime proxies (Castle.DynamicProxy in Moq) — slow creation and verification in big test suites.
  • Static/sealed mocking hell — no native support without paid tools or wrappers.
  • Async and generics issues — unexpected null tasks or clunky type inference.
  • Brittle tests — mocks that pass locally but fail in production because they don't match real API shapes.

Moq is battle-tested (and had its share of drama), NSubstitute is clean and readable, but neither fully leverages modern .NET features like source generators for zero overhead.

NimbleMock fixes these with compile-time magic: no runtime proxies, aggressive inlining, and stack allocation. Result? 34x faster mock creation and 3x faster verification than Moq, with zero allocations in typical scenarios.

Key Features

  • Partial mocks: Only mock what you need — unmocked methods throw clear NotImplementedException for early detection.
  • Native static/sealed mocking: Mock DateTime.Now, Environment, etc., directly.
  • First-class async support: Seamless for Task<T> and ValueTask<T>.
  • Generic type inference: Full support for nested generics (e.g., EF Core IQueryable<T>).
  • Fluent, chainable API: Inspired by the best of NSubstitute and Moq.
  • Lie-proofing: Optional runtime validation against real staging APIs to catch outdated mock shapes.
  • Compile-time analyzers: Catch errors early (e.g., warn if you forget SetupAsync for async methods).

MIT-licensed, open-source, and ready for contributions!

Quick Start

Install via NuGet:

dotnet add package NimbleMock
Enter fullscreen mode Exit fullscreen mode

Basic example:

using NimbleMock;

public interface IUserRepository
{
    User GetById(int id);
    Task<bool> SaveAsync(User user);
}

var mock = Mock.Of<IUserRepository>()
    .Setup(x => x.GetById(1), new User { Id = 1, Name = "Alice" })
    .SetupAsync(x => x.SaveAsync(default!), true)
    .Build();

var user = mock.Object.GetById(1); // Returns Alice

mock.Verify(x => x.GetById(1)).Once();
Enter fullscreen mode Exit fullscreen mode

Partial mock (perfect for god-interfaces):

var mock = Mock.Partial<ILargeService>()
    .Only(x => x.GetData(1), expectedData)
    .Build();

// Unmocked methods throw for safety
Enter fullscreen mode Exit fullscreen mode

Static mocking:

var staticMock = Mock.Static<DateTime>()
    .Returns(d => d.Now, new DateTime(2025, 12, 25))
    .Build();

staticMock.Verify(d => d.Now).Once();
Enter fullscreen mode Exit fullscreen mode

Generics example:

public interface IRepository<T>
{
    IQueryable<T> Query();
}

var mock = Mock.Of<IRepository<User>>()
    .Setup(x => x.Query(), users.AsQueryable())
    .Build();

var results = mock.Object.Query().Where(u => u.IsActive).ToList();
Enter fullscreen mode Exit fullscreen mode

Performance Benchmarks

Run on .NET 8 (BenchmarkDotNet):

Mock Creation & Setup

Library Time (ns) Memory Allocated Gain vs Moq
Moq 48,812 10.37 KB Baseline
NSubstitute 9,937 12.36 KB ~5x faster
NimbleMock 1,415 3.45 KB 34x faster than Moq

Method Execution Overhead

Library Time (μs) Gain vs Moq
Moq ~1.4 Baseline
NSubstitute ~1.6 Slower
NimbleMock ~0.6 2.3x faster

Verification

Library Time (ns) Memory Allocated Gain vs Moq
Moq 1,795 2.12 KB Baseline
NSubstitute 2,163 2.82 KB Slower
NimbleMock 585 0.53 KB 3x faster than Moq

Powered by source generators — no runtime reflection!

Run them yourself:

dotnet run --project tests/NimbleMock.Benchmarks --configuration Release
Enter fullscreen mode Exit fullscreen mode

Migrating from Moq/NSubstitute

It's straightforward — fluent chains make it cleaner.

From Moq:

// Moq
var mock = new Mock<IUserRepository>();
mock.Setup(x => x.GetById(1)).Returns(user);

// NimbleMock
var mock = Mock.Of<IUserRepository>()
    .Setup(x => x.GetById(1), user)
    .Build();
Enter fullscreen mode Exit fullscreen mode

Verification is more readable too.

Full migration guide in the repo!

Lie-Proofing: Avoid "Tests That Lie"

Unique feature: Validate mocks against real endpoints.

var result = await LieProofing.AssertMatchesReal<IApiService>("https://api.staging.example.com");

if (!result.IsValid)
{
    // Log mismatches
}
Enter fullscreen mode Exit fullscreen mode

Catches drifting APIs before production outages.

Final Thoughts

NimbleMock is built for .NET 8/9+ era: fast, safe, and joyful TDD. If you're tired of slow suites or static mocking hacks, give it a try!

Feedback welcome — what pains do you have with current mocking libs? Would you switch for native statics and this speed?

Star if you like it, and let's make testing fun again! 🚀

Tags: #dotnet #csharp #testing #tdd #opensource #mocking #performance

Top comments (0)