A cheat sheet to migrate from Moq to NSubstitute

profile
Tim Deschryver
timdeschryver.dev

Initialization: new Mock<T>() => Substitute.For<T>()

Argument matchers

Matches any value: It.IsAny<T>() => Arg.Any<T>()

Matches a specific value: It.Is<T>() => Arg.Is<T>()

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

Method invoked with any arguments

Method invoked with specific arguments

Number of invocations: Times.Exactly(N) => Received(N)

Is not invoked: Times.Never() => DidNotReceive()

Reset invocations: Reset() => ClearReceivedCalls()

Task invocation

Setting the return value

Static return value: Setup().Returns() => Returns()

Return value based on input: method arguments => callInfo

Fine grained control over return value based on input

Async (Task) return value: ReturnsAsync() => Returns()

Multiple return values: SetupSequence().Returns() => Returns()

Callbacks: Callback() => AndDoes()

Callback for void methods (or methods without a defined result): Callback() => When().Do()

Throwing Exceptions: Throws() => When().Do(() => throw)

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:

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!

Join My Newsletter (WIP)

Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at ko-fi.com PayPal logo

Share this post