Mokk 0.2.3
dotnet add package Mokk --version 0.2.3
NuGet\Install-Package Mokk -Version 0.2.3
<PackageReference Include="Mokk" Version="0.2.3" />
<PackageVersion Include="Mokk" Version="0.2.3" />
<PackageReference Include="Mokk" />
paket add Mokk --version 0.2.3
#r "nuget: Mokk, 0.2.3"
#:package Mokk@0.2.3
#addin nuget:?package=Mokk&version=0.2.3
#tool nuget:?package=Mokk&version=0.2.3
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 | Versions 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. |
-
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.