Mokk 0.2.3

dotnet add package Mokk --version 0.2.3
                    
NuGet\Install-Package Mokk -Version 0.2.3
                    
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="Mokk" Version="0.2.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mokk" Version="0.2.3" />
                    
Directory.Packages.props
<PackageReference Include="Mokk" />
                    
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 Mokk --version 0.2.3
                    
#r "nuget: Mokk, 0.2.3"
                    
#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 Mokk@0.2.3
                    
#: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=Mokk&version=0.2.3
                    
Install as a Cake Addin
#tool nuget:?package=Mokk&version=0.2.3
                    
Install as a Cake Tool

Mokk

<div align="center"> <img src="https://count.getloli.com/get/@mircodz-mokk?theme=asoul&padding=3" /><br> </div>

Introduction

C# mocking library powered by Roslyn source generators.

Installation

dotnet add package Mokk

The package includes both the runtime library and the source generator.

[assembly: GenerateMock(typeof(IEmailService))]

var mock = new MockEmailService();

mock.Send(Any, Any).Returns(true);
mock.GetTemplate("welcome", Any).Returns("Hi there!");

mock.Instance.Send("alice@example.com", "Welcome!");

mock.Send(Any, Any).Verify(Times.Once);

Setup

using Mokk;

[assembly: GenerateMock(typeof(IEmailService))]
[assembly: GenerateMock(typeof(IUserRepository))]
[assembly: GenerateMock(typeof(AbstractNotificationService))]

Factory

var mock = new MockEmailService();
var mock = IEmailService.Mock(); // when compiling with C# 14+

Matchers

using static Mokk.Wildcard;

mock.Send(Any, Any).Returns(true);
mock.Send("alice@example.com", Any).Returns(true);
mock.Send(Arg.Is<string>(s => s.EndsWith(".com")), Any).Returns(true);

mock.Send(Arg.Like("*@example.com"), Any).Returns(true);
mock.Send(Arg.Regex(@"^\S+@\S+$"), Any).Returns(true);
mock.Send(Arg.NotNull<string>(), Any).Returns(true);
mock.GetTemplate(Arg.In("a", "b"), Arg.InRange(1, 3)).Returns("ok");

Returns

mock.GetTemplate(Any, Any).Returns("hello");
mock.GetTemplate(Any, Any).Returns(() => ComputeValue());
mock.GetTemplate(Any, Any).Returns((string name, int ver) => $"{name}-v{ver}");

Callbacks and Throws

mock.Send(Any, Any)
    .Callback((string to, string subject) => log.Add($"{to}: {subject}"))
    .Returns(true);

mock.Send("bad@actor.com", Any).Throws<UnauthorizedException>();

Sequence setup

mock.GetTemplate(Any, Any).Sequence()
    .Returns("first response")
    .Returns("second response")
    .Throws(new Exception("exhausted"));

Async

mock.GetUserAsync(Any).ReturnsAsync("Alice");
mock.GetUserAsync(Any).ReturnsAsync(() => Compute());
mock.GetUserAsync(Any).ThrowsAsync(new IOException()); // faulted task, not a sync throw

mock.GetUserAsync(Any).Sequence()
    .ReturnsAsync("first")
    .ThrowsAsync(new Exception("exhausted"));

ref / out / in

// out values are set from a callback; out params drop out of the setup signature
mock.TryParse("42").Callback(args => args[1] = 42).Returns(true);
mock.Instance.TryParse("42", out int value); // value == 42

mock.Increment(Any).Callback(args => args[0] = (int)args[0]! + 1);

Generic methods

mock.DoSomething<int>(Any).Returns(42);
mock.DoSomething<string>(Any).Returns("hello");
mock.DoSomething<AnyType>(Any).Callback(() => count++); // matches any T

Generic types

[assembly: GenerateMock(typeof(IMessage<,>))] // closed types collapse to this too

var mock = new MockMessage<string, int>();
mock.Get("answer").Returns(42);

Properties

// Auto-backed
mock.Instance.Name = "Alice";
Assert.Equal("Alice", mock.Instance.Name);

mock.Name.Getter().Returns("Alice");
mock.Name.Setter(Any).Verify(Times.Once);

Events

mock.Instance.UserChanged += (sender, e) => ...;

mock.UserChanged.Raise(mock.Instance, new UserChangedEventArgs(42));
mock.AuditLogged.Raise(7, "deleted"); // custom delegate: positional args
Assert.Equal(1, mock.UserChanged.SubscriberCount);

mock.UserChanged.Subscribed(handler, Times.Once);
mock.UserChanged.Unsubscribed(Times.Never);
mock.UserChanged.HandlerInvoked(handler, Times.Exactly(2));

Abstract-class mocks expose the raiser as {EventName}Handle.

Indexers

mock.Indexer("apple").Getter().Returns(5);              // any mock
mock[Arg<string>.Any()].Setter().Callback((k, v) => { });  // interface mocks

Verify

mock.Send(Any, Any).Verify(Times.Once);
mock.Send(Any, Any).Verify(Times.Never);
mock.Send(Any, Any).Verify(Times.Exactly(3));
mock.Send(Any, Any).Verify(Times.AtLeast(2));
mock.Send(Any, Any).Verify(Times.AtMost(5));
mock.Send(Any, Any).Verify(Times.Between(2, 5));

Verify throws VerificationException on failure.

VerifyInOrder

Assert relative call order on a single mock:

mock.VerifyInOrder(
    mock.Login(Any),
    mock.GetUser(Any),
    mock.Logout()
);

VerifyInOrder across mocks

A MockSession records calls across multiple mocks on one timeline, so order can be asserted across mock boundaries:

var session = new MockSession(auth, audit);

sut.Run();

session.VerifyInOrder(
    auth.Login(Any),
    audit.Write(Any),
    auth.Logout()
);

VerifyNoOtherCalls

mock.Send(Any, Any).Returns(true);
mock.Instance.Send("a@b.com", "hi");

mock.Send(Any, Any).Verify(Times.Once);
mock.VerifyNoOtherCalls(); // passes — all calls were covered

ReceivedCalls

Programmatic access to the call log:

var calls = mock.Send(Any, Any).ReceivedCalls();
Assert.Equal(2, calls.Count);
Assert.Equal("alice@example.com", (string)calls[0].Args[0]);

Argument capture

using static Mokk.Capture;

var slot = Slot<string>();
mock.Send(Into(slot), Any).Returns(true);

mock.Instance.Send("hello@test.com", "subject");

Assert.Equal("hello@test.com", slot.Value);

Wrapping

var mock = new MockEmailService(wrapping: new RealEmailService(smtpClient));

// Overrides one method; all others delegate to the real implementation
mock.GetTemplate("welcome", 1).Returns("cached");

mock.Instance.Send("alice@example.com", "hi"); // calls real Send
mock.Send(Any, Any).Verify(Times.Once);

Abstract class mocks

[assembly: GenerateMock(typeof(AbstractNotificationService))]

var mock = new MockNotificationService();
mock.Notify(Any, Any).Returns(true);

mock.ServiceNameHandle.Getter().Returns("test-service");

Assert.True(mock.Instance.Notify("user@test.com", "Hello"));
mock.Notify(Any, Any).Verify(Times.Once);

Strict mode

var mock = new MockEmailService(strict: true);
mock.Instance.Send("a@b.com", "hi"); // throws MissingSetupException — no setup matched

Unused setup warnings

var warnings = new List<string>();
var mock = new MockEmailService(onUnusedSetup: warnings.Add);

mock.GetTemplate(Any, Any).Returns("Hi!");
mock.Instance.Send("a@b.com", "hello");

mock.CheckUnusedSetups();

Reset

mock.Reset();

Benchmarks

BenchmarkDotNet v0.15.8, .NET 8.0, Linux, 12th Gen Intel Core i7-12700KF

| Method      | Mean       | Error      | StdDev     | Rank | Gen0   | Gen1   | Allocated |
|------------ |-----------:|-----------:|-----------:|-----:|-------:|-------:|----------:|
| Mokk        | 164.042 ns |  8.3301 ns |  4.3568 ns |    1 | 0.0219 | 0.0055 |    1056 B |
| Imposter    | 202.634 ns | 11.0567 ns |  7.3133 ns |    2 | 0.0124 | 0.0119 |     168 B |
| Moq         | 290.735 ns | 14.6240 ns |  9.6729 ns |    3 | 0.0205 | 0.0100 |     528 B |
| NSubstitute | 357.505 ns | 11.7103 ns |  7.7456 ns |    4 | 0.0219 | 0.0110 |     304 B |
| FakeItEasy  | 953.988 ns | 30.6226 ns | 20.2550 ns |    5 | 0.0591 | 0.0572 |     808 B |
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 was computed.  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.
  • net8.0

    • No dependencies.

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.2.3 91 5/18/2026
0.2.2 89 5/17/2026
0.2.1 87 5/17/2026
0.2.0 95 5/15/2026
0.1.2 113 3/19/2026