A cheat sheet to migrate from Moq to NSubstitute
Initialization: new Mock<T>() => Substitute.For<T>()
Moq.csvar mock = new Mock<IService>();// or, if you don't need to customize and verify the mockvar mock = Mock.Of<IService>();// when using the above, you can still get the mock instance using Mock.Getvar mockInstance = Mock.Get(mock);
NSubstitute.csvar mock = Substitute.For<IService>();
Argument matchers
Matches any value: It.IsAny<T>() => Arg.Any<T>()
Moq.csIt.IsAny<string>()
NSubstitute.csArg.Any<string>()// shorthand: use `default` im combination with `WithAnyArgs` methods// instead of the `Arg` syntax in (examples follow later)
Matches a specific value: It.Is<T>() => Arg.Is<T>()
Moq.csIt.Is<string>(matcher => matcher == "foo")It.Is<int>(matcher => matcher >= 10 && matcher <= 20)
NSubstitute.csArg.Is<string>(matcher => matcher == "foo")Arg.Is<string>("foo")Arg.Is<int>(matcher => matcher >= 10 && matcher <= 20)// shorthand: use `value` directly instead of the `Arg` syntax (examples follow later)
Matches a generic type: It.IsAnyType() => TBD
Moq's helpers It.IsAnyType, It.IsValueType, and It.IsSubtype<T> are currently not supported by NSubstitute.
Keep an eye on this issue #634 for updates.
Testing method invocations: Verify() => Received()
Method without arguments
Moq.csvar mock = new Mock<IMyDependency>();mock.Object.Method();mock.Verify(x => x.Method());
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.Method();mock.Received().Method();
Method invoked with any arguments
Moq.csvar mock = new Mock<IMyDependency>();mock.Object.MethodWithArgument("foo");mock.Verify(x => x.MethodWithArgument(It.IsAny<string>()));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithArgument("foo");mock.Received().MethodWithArgument(Arg.Any<string>());// or use `ReceivedWithAnyArgs` in combination with `default` instead of the `Arg` syntaxmock.ReceivedWithAnyArgs().MethodWithArgument(default);
Method invoked with specific arguments
Moq.csvar mock = new Mock<IMyDependency>();mock.Object.MethodWithArgument("foo");mock.Verify(x => x.MethodWithArgument(It.Is<string>(match => match == "foo")));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithArgument("foo");mock.Received().MethodWithArgument(Arg.Is<string>(match => match == "foo"));// shorthand for the `Arg` syntaxmock.Received().MethodWithArgument(Arg.Is("foo"));// or directly use the value instead of the `Arg` syntaxmock.Received().MethodWithArgument("foo");
Number of invocations: Times.Exactly(N) => Received(N)
Moq.csvar mock = new Mock<IMyDependency>();mock.MethodWithArgument("foo");mock.MethodWithArgument("bar");mock.MethodWithArgument("bar");// is invokedmock.Verify(x => x.MethodWithArgument(It.IsAny<string>()));// is invoked exactly oncemock.Verify(x => x.MethodWithArgument(It.Is<string>(match => match == "foo")), Times.Once());// is invoked exactly x timesmock.Verify(x => x.MethodWithArgument(It.Is<string>(match => match == "bar")), Times.Exactly(2));// there are also other options like `Times.Between(N, N)`, `Times.AtMost(N)`, `Times.AtLeast(N)`// these do not have a direct equivalent in NSubstitute
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithArgument("foo");mock.MethodWithArgument("bar");mock.MethodWithArgument("bar");// is calledmock.ReceivedWithAnyArgs().MethodWithArgument(default);// is called exactly oncemock.Received(1).MethodWithArgument(Arg.Is<string>(match => match == "foo"));// is called x timesmock.Received(2).MethodWithArgument(Arg.Is<string>(match => match == "bar"));
Is not invoked: Times.Never() => DidNotReceive()
Moq.csvar mock = new Mock<IMyDependency>();mock.Object.MethodWithArgument("foo");mock.Verify(x => x.Method(), Times.Never());mock.Verify(x => x.MethodWithArgument(It.Is<string>(match => match == "bar")), Times.Never());
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithArgument("foo");mock.DidNotReceiveWithAnyArgs().Method();// with argument checkmock.DidNotReceive().MethodWithArgument(Arg.Is<string>(match => match == "bar"));mock.DidNotReceive().MethodWithArgument(Arg.Is<string>("bar"));// shorthand for the `Arg` syntaxmock.DidNotReceive().MethodWithArgument("bar");
Reset invocations: Reset() => ClearReceivedCalls()
Moq.csvar mock = new Mock<IMyDependency>();mock.Object.Method();mock.Verify(x => x.Method());mock.Reset();mock.Verify(x => x.Method(), Times.Never());
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.Method();mock.Received().Method();mock.ClearReceivedCalls();mock.DidNotReceive().Method();
Task invocation
Moq.csvar mock = new Mock<IMyDependency>();await mock.Object.MethodAsync();mock.Verify(x => x.MethodAsync());
NSubstitute.csvar mock = Substitute.For<IMyDependency>();await mock.MethodAsync();// also works without `await` but results in a warningawait mock.Received().MethodAsync();
Setting the return value
Static return value: Setup().Returns() => Returns()
Moq.csvar mock = new Mock<IMyDependency>();mock.Setup(m => m.MethodWithReturnValue()).Returns("output");var result = mock.Object.MethodWithReturnValue();Assert.That(result, Is.EqualTo("output"));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithReturnValue().Returns("output");var result = mock.MethodWithReturnValue();Assert.That(result, Is.EqualTo("output"));
Return value based on input: method arguments => callInfo
Moq.csvar mock = new Mock<IMyDependency>();mock.Setup(m => m.MethodWithReturnValue(It.IsAny<int>(), It.IsAny<string>())).Returns((int input, string prefix) => $"{prefix}_{input}");var result = mock.Object.MethodWithReturnValue(1, "prefix");Assert.That(result, Is.EqualTo("prefix_1"));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();// get the argument based on the type, and if needed include the indexmock.MethodWithReturnValue(Arg.Any<int>(), Arg.Any<string>()).Returns(callInfo => $"{callInfo.Arg<string>()}_{callInfo.ArgAt<int>(0)}");// using `ReturnsForAnyArgs` in combination with `default` instead of the `Arg` syntaxmock.MethodWithReturnValue(default, default).ReturnsForAnyArgs(callInfo => $"{callInfo.Arg<string>()}_{callInfo.ArgAt<int>(0)}");var result = mock.MethodWithReturnValue(1, "prefix");Assert.That(result, Is.EqualTo("prefix_1"));
Fine grained control over return value based on input
Moq.csvar mock = new Mock<IMyDependency>();mock.Setup(m => m.MethodWithReturnValue(It.IsAny<int>(), It.IsAny<string>())).Returns((int input, string prefix) => $"{prefix}_{input}");mock.Setup(m => m.MethodWithReturnValue(It.Is<int>(i => i == 1), It.IsAny<string>())).Returns((int input, string prefix) => $"special_{prefix}_{input}");var result1 = mock.Object.MethodWithReturnValue(1, "prefix");// matches the second setup, its return value is usedAssert.That(result1, Is.EqualTo("special_prefix_1"));var result2 = mock.Object.MethodWithReturnValue(2, "prefix");// doesn't match the second setup so it uses the return value of the first setupAssert.That(result2, Is.EqualTo("prefix_2"));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithReturnValue(Arg.Any<int>(), "prefix").Returns(callInfo => $"{callInfo.Arg<string>()}_{callInfo.ArgAt<int>(0)}");mock.MethodWithReturnValue(1, "prefix").Returns(callInfo => $"special_{callInfo.Arg<string>()}_{callInfo.ArgAt<int>(0)}");// fallback: ReturnsForAll is used to set the return value for all calls// that don't have a specific setup (if the return type matches the generic)mock.ReturnsForAll<string>("fallback");// fallback: using the callInfo to create the return valuemock.ReturnsForAll<string>(callInfo => $"{callInfo.Arg<string>()}_{callInfo.Arg<int>()}");// NOTE: the callbacks are invoked in the order they are defined// the last return value is used as the return value of the method// this can become problematic when using callbacks that have side effects (more on this later)// as a workaround, use `Configure` to ensure previous callbacks do not run// e.g. this is useful when previous callbacks throw exceptionsmock.Configure().MethodWithReturnValue(10, "prefix").Returns("prefix_10");var result1 = mock.MethodWithReturnValue(1, "prefix");// matches the second setup, its return value is usedAssert.That(result1, Is.EqualTo("special_prefix_1"));var result2 = mock.MethodWithReturnValue(2, "prefix");// doesn't match the second setup so it uses the return value of the first setupAssert.That(result2, Is.EqualTo("prefix_2"));var result3 = mock.MethodWithReturnValue(1, "other");// doesn't match any setup, thus the fallback is usedAssert.That(result3, Is.EqualTo("other_1"));
Async (Task) return value: ReturnsAsync() => Returns()
Moq.csvar mock = new Mock<IMyDependency>();mock.Setup(m => m.MethodWithReturnValueAsync(It.IsAny<long>())).ReturnsAsync("output");var result = await mock.Object.MethodWithReturnValueAsync(1);Assert.That(result, Is.EqualTo("output"));mock.Verify(x => x.MethodWithReturnValueAsync(It.IsAny<long>()), Times.Once());
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithReturnValueAsync(default).ReturnsForAnyArgs("output");var result = await mock.MethodWithReturnValueAsync(1);Assert.That(result, Is.EqualTo("output"));await mock.ReceivedWithAnyArgs(1).MethodWithReturnValueAsync(default);
Multiple return values: SetupSequence().Returns() => Returns()
Moq.csvar mock = new Mock<IMyDependency>();mock.SetupSequence(m => m.MethodWithReturnValue(It.IsAny<int>())).Returns("one").Returns("two");var result1 = mock.Object.MethodWithReturnValue(1);var result2 = mock.Object.MethodWithReturnValue(2);var result3 = mock.Object.MethodWithReturnValue(3);Assert.That(result1, Is.EqualTo("one"));Assert.That(result2, Is.EqualTo("two"));Assert.That(result3, Is.Null);
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.MethodWithReturnValue(default).ReturnsForAnyArgs("one", "two");var result1 = mock.MethodWithReturnValue(1);var result2 = mock.MethodWithReturnValue(2);var result3 = mock.MethodWithReturnValue(3);Assert.That(result1, Is.EqualTo("one"));Assert.That(result2, Is.EqualTo("two"));// 👇 different from Moq, NSubstitute will return the last value for all subsequent callsAssert.That(result3, Is.EqualTo("two"));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();// also supports multiple return values based on the inputmock.MethodWithReturnValue(default).ReturnsForAnyArgs(callInfo => callInfo.Arg<int>().ToString(), callInfo => callInfo.Arg<int>().ToString());var result1 = mock.MethodWithReturnValue(1);var result2 = mock.MethodWithReturnValue(2);var result3 = mock.MethodWithReturnValue(3);Assert.That(result1, Is.EqualTo("1"));Assert.That(result2, Is.EqualTo("2"));Assert.That(result3, Is.EqualTo("3"));
Callbacks: Callback() => AndDoes()
Moq.csvar mock = new Mock<IMyDependency>();var result = "";mock.Setup(m => m.MethodWithReturnValue(It.IsAny<int>())).Returns("output").Callback((int x) =>{result = $"callback {x}";});mock.Object.MethodWithReturnValue(1);Assert.That(result, Is.EqualTo("callback 1"));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();var result = "";mock.MethodWithReturnValue(default).ReturnsForAnyArgs("output").AndDoes(callInfo =>{result = $"callback {callInfo.Arg<int>()}";});mock.MethodWithReturnValue(1);Assert.That(result, Is.EqualTo("callback 1"));
Callback for void methods (or methods without a defined result): Callback() => When().Do()
Moq.csvar mock = new Mock<IMyDependency>();var result = "";mock.Setup(m => m.MethodWithReturnValue(It.IsAny<int>())).Callback((int x) =>{result = $"callback {x}";});mock.Object.MethodWithReturnValue(1);Assert.That(result, Is.EqualTo("callback 1"));
NSubstitute.csvar mock = Substitute.For<IMyDependency>();var result = "";mock.WhenForAnyArgs(m => m.MethodWithReturnValue(default)).Do(callInfo =>{result = $"callback {callInfo.Arg<int>()}";});mock.MethodWithReturnValue(1);Assert.That(result, Is.EqualTo("callback 1"));
Throwing Exceptions: Throws() => When().Do(() => throw)
Moq.csvar mock = new Mock<IMyDependency>();mock.Setup(m => m.Method()).Throws(new InvalidOperationException());Assert.Throws<InvalidOperationException>(() => mock.Object.Method());
NSubstitute.csvar mock = Substitute.For<IMyDependency>();mock.When(w => w.Method()).Do((callInfo) => throw new InvalidOperationException());Assert.Throws<InvalidOperationException>(() => mock.Method());
Automate Migration
Because both syntaxes resemble each other, it is possible to use a couple of smart find and (RegExp) replace commands to help with the migration. These are not perfect, but they can be an enoourmous boost to get you started, and migrate 80% of your codebase in a matter of minutes.
Here are a few that I used. Before using them, make sure that you're working on a clean branch.
| Find | Replace |
|---|---|
using Moq |
using NSubstitute |
new Mock<(.*)>\(\) |
Substitute.For<$1>() |
Mock<(.*?)> |
$1 |
\.Setup\([\w\s]*=>[\w\s]*(.*)\) |
$1 |
\.ReturnsAsync\( |
.Returns( |
It\.IsAny |
Arg.Any |
It\.Is |
Arg.Is |
\.Object |
(be careful here as it may also replace other non-moq properties) |
((\w+)|(\w+\s+))\.Verify\([\w\s]*=>[\w\s]*\.(.*)\)((.*?)), Times.Never\(\)\) |
await $1.DidNotReceive().$4) |
\.Verify\([\w\s]*=>[\w\s]*\.(.*)\)((.*?)), |
.DidNotReceive().$1) |
((\w+)|(\w+\s+))\.Verify\([\w\s]*=>[\w\s]*\.(.*)\)((.*?)), Times\.(Once(\(\))?|Exactly\((?<times>.*)\))\) |
await $1.Received(${times}).$4) |
\.Verify\([\w\s]*=>[\w\s]*\.(.*)\)((.*?)), Times\.(Once(\(\))?|Exactly\((?<times>.*)\))\) |
Received(${times}).$1) |
new AutoMoqCustomization |
new AutoNSubstituteCustomization |
using AutoFixture\.AutoMoq |
using AutoFixture.AutoNSubstitute |
I based mine implementation of these expression on the following resources:
- https://github.com/AlbertoMonteiro/moq-to-nsubstitute
- https://github.com/moq/moq/issues/1374#issuecomment-1671241122
Conclusion
In this cheat sheet, we've seen the syntax differences between Moq and NSubstitute, more specifically how to convert Moq code to NSubstitute code. We've also touched on how to automate the migration of your codebase.
What I like about NSubstitute is that it's a lot more lightweight than Moq, and it doesn't require a lot of wrapping to mock interfaces. This makes the syntax more compact and easier to read.
For more information on NSubstitute, take a look at the NSubstitute documentation.
Feel free to update this blog post on GitHub, thanks in advance!
Enjoying the blog?
Support my work
If you enjoyed this post and found it useful, consider supporting my work. It helps me keep creating and sharing content like this. Thank you!