Effective mocking

Recently, I’ve been asked if Roslyn can be used for helping with writing code that involves preparing mocks with NSubstitute. Of course, my answer was "Yes" but instead of rushing into creating a new project that would implement this functionality, I performed a small research. I checked nuget.org and Visual Studio extensions marketplace, and I discovered that there was a bunch of existing analyzers and extensions that facilitated working with mocks and not only for NSubstitute but for other mocking frameworks, just like Moq or Fake It Easy, too. In this blog post, I will show you how these tools are helping to avoid common problems with mocking and boost your productivity by saving you a lot of typing. Presented examples will be mostly based on the Moq library because it’s my favoring mocking package.

Moving runtime problems to design time 🔗︎

The idea behind mocking is to create in runtime a type that inherits from the mocked type and behaves in a way that we specify in mock configuration. The most common problem with mocking is that not everything related to mock preparation can be verified in design time, and we need to compile and run our test in order to verify if the mock was constructed correctly. For example, from the semantical and syntactical point of view, it’s possible to write a code that creates mock of sealed class or for a non-overridable method (sealed or non-abstract, or non-virtual). However, when we run a test that uses such kind of mock we’ve got the exception:

Exception when non-overridable used in setup

With Moq.Analyzers we can detect these problems in design time, right at the moment when we type the code that violates these rules.

Roslyn error when non-overridable used in setup

Another problem with defining expected behavior for the mocked method is that the delegate provided for Returns() should match the signature of the mocked method. There is no way to express such kind of constraint with C# syntax, so this code will compile but won’t work:

Example of invalid lambda passed to return method

Thanks to Roslyn analyzer, the feedback loop can be shortened by moving this verification from runtime to design time:

Roslyn error when invalid lambda passed to return method

The violations described here always result with runtime exception so it’s good to set up the severity level of corresponding verification rules to ERROR:

Configure roslyn rule severity level

If the Moq is not your mocking framework of choice don’t worry, there’s certainly a similar analyzer for your library. I was able to also find analyzers such as NSubstitute.Analyzers.CSharp and FakeItEasy.Analyzer.CSharp

Less typing to create a mock 🔗︎

Besides all those traps with mocking rules, preparing mocks involves a lot of typing which is a dummy work. For example, to mock a method with three parameters (without carrying for their values) we need to write a code like this:

var mock = new Mock<ISampleInterface>();
mock.Setup(m => m.DoSomething(It.IsAny<int>(), It.IsAny<decimal>(), It.IsAny<string>()))
    .Returns((p1, p2, p3)=>  EXPECT_VALUE);

Typing repeatable It.IsAny<>() and trying to match the method signature in Returns() is quite boring and error-prone. Happily, these issues can be solved with Visual Studio extension - Moq.Autocomplete that can propose and insert the whole code for us.

If the suggestions are not appearing after pressing ctr+space you should try to press ctr+j (yes, I lost a couple of hours before I discovered that). Moq.Autocomplete works only for Moq and Visual Studio 2019. However, if you are using other mocking frameworks or still working on Visual Studio 2017, you can try Mocking.Helpers extension - it works for NSubstitute, FakeItEasy and Moq too but only provides suggestions for mocked method parameters.

If you need to return from the mock a sample object that has many properties that should be initialized with sample values, you can use MappingGenerator that can easily scaffold object initialization:

Automate object scaffolding with Roslyn extension

Products recommended for highly effective .NET Developers:

comments powered by Disqus

See Also