Mockolate.Migration
0.5.0
Prefix Reserved
dotnet add package Mockolate.Migration --version 0.5.0
NuGet\Install-Package Mockolate.Migration -Version 0.5.0
<PackageReference Include="Mockolate.Migration" Version="0.5.0" />
<PackageVersion Include="Mockolate.Migration" Version="0.5.0" />
<PackageReference Include="Mockolate.Migration" />
paket add Mockolate.Migration --version 0.5.0
#r "nuget: Mockolate.Migration, 0.5.0"
#:package Mockolate.Migration@0.5.0
#addin nuget:?package=Mockolate.Migration&version=0.5.0
#tool nuget:?package=Mockolate.Migration&version=0.5.0
Mockolate.Migration
A Roslyn analyzer and code-fix provider that migrates Moq and NSubstitute usage to Mockolate. Drop the package into a project that uses one of those libraries and the analyzer flags each mock it can migrate; the accompanying code fix rewrites the setup, verification, and event APIs to their Mockolate equivalents.
Installation
Install the NuGet package into the project you want to migrate:
dotnet add package Mockolate.Migration
The package only needs to be referenced while you are migrating — it ships the analyzer and code fixer, not runtime code. Once the source library is gone from a project you can remove the reference again.
How it works
After installing the package, every supported construct is reported as a warning. Apply the relevant
code fix from your IDE (Visual Studio, Rider, VS Code with C# Dev Kit) or via
dotnet format analyzers to rewrite the call site.
| Diagnostic | Source library | Code fix title |
|---|---|---|
MockolateM001 |
Moq | Migrate Moq to Mockolate |
MockolateM002 |
NSubstitute | Migrate NSubstitute to Mockolate |
The fixer rewrites the whole mock — the construction call, all setup calls on that mock, the
corresponding verification calls, event wiring, and trailing .Object accesses (Moq) — in a single
step.
Migrating from Moq
The Moq fixer rewrites new Mock<T>(...) to T.CreateMock(...), drops every trailing .Object access, and
moves setups and verifications onto the strongly-typed Mock.Setup / Mock.Verify chains. Strict mocks are
mapped to MockBehavior.Default.ThrowingWhenNotSetup(), It.Is<T>(predicate) becomes It.Satisfies<T>(...),
and event raising / verification are routed through Mock.Raise and event-specific
Mock.Verify.<Event>.Subscribed() / Unsubscribed() calls.
// Before (Moq)
var mock = new Mock<IFoo>();
mock.Setup(m => m.Compute(It.IsAny<int>())).Returns(42);
var sut = new Service(mock.Object);
sut.Run();
mock.Verify(m => m.Compute(It.IsAny<int>()), Times.Once());
// After (Mockolate)
var mock = IFoo.CreateMock();
mock.Mock.Setup.Compute(It.IsAny<int>()).Returns(42);
var sut = new Service(mock);
sut.Run();
mock.Mock.Verify.Compute(It.IsAny<int>()).Once();
// Before (Moq) — properties and events
var mock = new Mock<IFoo>(MockBehavior.Strict);
mock.SetupProperty(m => m.Name, "initial");
mock.Raise(m => m.Changed += null, EventArgs.Empty);
mock.VerifyAdd(m => m.Changed += It.IsAny<EventHandler>(), Times.Once());
// After (Mockolate)
var mock = IFoo.CreateMock(MockBehavior.Default.ThrowingWhenNotSetup());
mock.Mock.Setup.Name.InitializeWith("initial");
mock.Mock.Raise.Changed(null, EventArgs.Empty);
mock.Mock.Verify.Changed.Subscribed().Once();
Supported Moq migrations
| Moq construct | Rewritten to |
|---|---|
new Mock<IFoo>() |
IFoo.CreateMock() |
new Mock<IFoo>(MockBehavior.Strict) |
IFoo.CreateMock(MockBehavior.Default.ThrowingWhenNotSetup()) |
sut.Object |
sut (the .Object access is dropped) |
sut.Setup(m => m.Method(args)) |
sut.Mock.Setup.Method(args) |
sut.Setup(m => m.Prop) / sut.SetupGet(...) |
sut.Mock.Setup.Prop |
sut.SetupProperty(m => m.Prop) |
sut.Mock.Setup.Prop.Register() |
sut.SetupProperty(m => m.Prop, value) |
sut.Mock.Setup.Prop.InitializeWith(value) |
sut.SetupSequence(...).Returns(a).Returns(b) |
sut.Mock.Setup.Method(...).Returns(a).Returns(b) |
.Returns(...) / .ReturnsAsync(...) / .Throws(...) |
preserved on the Mockolate setup chain |
.Callback(...) |
.Do(...) on the Mockolate setup |
sut.InSequence(seq).Setup(...) (and other setup forms) |
.InSequence(...) wrapper is stripped; use Verify…Then(...) for ordering |
sut.Verify(m => m.Method(args), Times.Once()) |
sut.Mock.Verify.Method(args).Once() |
sut.VerifyGet / sut.VerifySet |
sut.Mock.Verify.Prop.Got(...) / .Set(...) |
sut.Raise(m => m.Event += null, args) |
sut.Mock.Raise.Event(sender, args) |
sut.VerifyAdd(m => m.Event += ..., times) |
sut.Mock.Verify.Event.Subscribed().<times>() |
sut.VerifyRemove(m => m.Event -= ..., times) |
sut.Mock.Verify.Event.Unsubscribed().<times>() |
It.IsAny<T>() |
preserved as It.IsAny<T>() |
It.Is<T>(predicate) |
It.Satisfies<T>(predicate) |
It.IsInRange(lo, hi, Range.Inclusive) |
It.IsInRange(lo, hi) (the Range argument is dropped) |
It.IsRegex(pattern, options) |
It.Matches(pattern).AsRegex(options) |
It.Ref<T>.IsAny / out parameters |
It.IsAnyRef<T>() / It.IsOut(() => value) |
Nested mocks (sut.Setup(m => m.Child.Prop)) |
Navigation chain is preserved: sut.Child.Mock.Setup.Prop |
Migrating from NSubstitute
The NSubstitute fixer rewrites Substitute.For<T>(...) to T.CreateMock(...) and reroutes the
implicit setup/verify calls (sub.Method(args).Returns(...), sub.Received().Method(...)) through the
strongly-typed Mock.Setup / Mock.Verify chains. Arg.Any<T>() becomes It.IsAny<T>(),
Arg.Is<T>(predicate) becomes It.Satisfies<T>(...), and Raise.Event(...) is rewritten to the
corresponding sub.Mock.Raise.<Event>(...) call. Nested-mock setups (sub.Child.M(args).Returns(...))
are migrated with a // TODO comment because the parent must be wired up to return the child mock manually.
// Before (NSubstitute)
var sub = Substitute.For<IFoo>();
sub.Compute(Arg.Any<int>()).Returns(42);
var sut = new Service(sub);
sut.Run();
sub.Received(1).Compute(Arg.Any<int>());
// After (Mockolate)
var sub = IFoo.CreateMock();
sub.Mock.Setup.Compute(It.IsAny<int>()).Returns(42);
var sut = new Service(sub);
sut.Run();
sub.Mock.Verify.Compute(It.IsAny<int>()).Once();
// Before (NSubstitute) — sequential returns, When/Do, events
var sub = Substitute.For<IFoo>();
sub.Compute(0).Returns(1, 2, 3);
sub.When(x => x.Reset()).Do(_ => counter++);
sub.Changed += Raise.EventWith(EventArgs.Empty);
// After (Mockolate)
var sub = IFoo.CreateMock();
sub.Mock.Setup.Compute(0).Returns(1).Returns(2).Returns(3);
sub.Mock.Setup.Reset().Do(() => counter++);
sub.Mock.Raise.Changed(null, EventArgs.Empty);
CallInfo callbacks
NSubstitute's Returns(call => …), When(...).Do(call => …), and AndDoes(call => …) callbacks
receive a CallInfo parameter that exposes the invocation arguments. Mockolate's equivalent
overloads instead take the method's parameters directly, so the migration rewrites the lambda:
- Body never reads the parameter — drop it:
Do(call => x++)→Do(() => x++). - Body uses statically resolvable accesses (
call.ArgAt<T>(literalIndex),call[literalIndex], or type-uniquecall.Arg<T>()) — rewrite the lambda's parameter list to match the receiver method and replace each access with the matching parameter name:Returns(call => call.ArgAt<int>(0) + 1)→Returns((int x) => x + 1). - Anything else — bare
call, dynamic indices, indexer-write for out/ref, ambiguouscall.Arg<T>(), or local variables that would shadow the injected parameter names — preserve the original lambda and emit a// TODOcomment so the rewrite can be done by hand.
Supported NSubstitute migrations
| NSubstitute construct | Rewritten to |
|---|---|
Substitute.For<IFoo>() |
IFoo.CreateMock() |
Substitute.For<IFoo>(args) |
IFoo.CreateMock(args) |
Substitute.For<IFoo, IBar>() |
IFoo.CreateMock().Implementing<IBar>() (chains for additional types) |
Substitute.ForPartsOf<MyClass>() |
MyClass.CreateMock() (Mockolate calls base by default) |
Substitute.ForTypeForwardingTo<TInterface, TClass>(args) |
TInterface.CreateMock().Wrapping(new TClass(args)) |
sub.Method(args).Returns(v) |
sub.Mock.Setup.Method(args).Returns(v) |
sub.Method(args).Returns(v1, v2, v3) |
sub.Mock.Setup.Method(args).Returns(v1).Returns(v2).Returns(v3) |
sub.Method(args).Throws<E>() / Throws(ex) |
sub.Mock.Setup.Method(args).Throws<E>() / .Throws(ex) |
sub.Method(args).ReturnsForAnyArgs(v) |
sub.Mock.Setup.Method(args).AnyParameters().Returns(v) |
sub.Method(args).ThrowsForAnyArgs<E>() |
sub.Mock.Setup.Method(args).AnyParameters().Throws<E>() |
sub.Method(args).Returns(v).AndDoes(cb) |
sub.Mock.Setup.Method(args).Returns(v).Do(cb) |
sub.Property.Returns(v) |
sub.Mock.Setup.Property.Returns(v) |
sub.Received().Method(args) |
sub.Mock.Verify.Method(args).AtLeastOnce() |
sub.Received(n).Method(args) |
sub.Mock.Verify.Method(args).Exactly(n) (Once() for n == 1) |
sub.DidNotReceive().Method(args) |
sub.Mock.Verify.Method(args).Never() |
sub.ReceivedWithAnyArgs().Method(default, default) |
sub.Mock.Verify.Method(default, default).AnyParameters().AtLeastOnce() |
sub.DidNotReceiveWithAnyArgs().Method(default, default) |
sub.Mock.Verify.Method(default, default).AnyParameters().Never() |
_ = sub.Received().Prop |
sub.Mock.Verify.Prop.Got().AtLeastOnce() |
sub.Received().Prop = v |
sub.Mock.Verify.Prop.Set(v).AtLeastOnce() |
sub.ClearReceivedCalls() |
sub.Mock.ClearAllInteractions() |
sub.MyEvent += Raise.Event() |
sub.Mock.Raise.MyEvent(null, EventArgs.Empty) |
sub.MyEvent += Raise.EventWith(sender, args) |
sub.Mock.Raise.MyEvent(sender, args) |
sub.MyEvent += Raise.EventWith(args) |
sub.Mock.Raise.MyEvent(null, args) |
sub.MyEvent += Raise.Event<TDelegate>(args...) |
sub.Mock.Raise.MyEvent(args...) (delegate type dropped) |
sub.When(x => x.M(args)).Do(cb) |
sub.Mock.Setup.M(args).Do(cb) |
sub.When(x => x.M(args)).DoNotCallBase() |
sub.Mock.Setup.M(args).SkippingBaseClass() |
Arg.Any<T>() |
It.IsAny<T>() |
Arg.Is<T>(predicate) |
It.Satisfies<T>(predicate) |
Arg.Is(value) / Arg.Is<T>(value) |
It.Is(value) / It.Is<T>(value) |
Arg.Compat.X<T>(...) |
same as the corresponding Arg.X<T>(...) |
Nested mocks (sub.Child.M(args).Returns(v)) |
sub.Child.Mock.Setup.M(args).Returns(v) plus a // TODO comment to register Child |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.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.