How does mock data work in .NET

Tymur Polishchuk
Lodgify Technology Blog
4 min readAug 16, 2023

Let’s analyze it using an example with code

Hello, everyone! During my daily work, I often write unit tests, and in almost every test, we use one library or another for creating mocks.

Once, I became curious about how it is possible that even if we don’t have a specific implementation of an interface (at all!), we can still make the function return the result we desire. In other words, ‘mock’ is a function.

I became interested in understanding how this works and decided to try writing a small library for function mocking. So, in this article, I’ll analyze it.

Let’s consider existing solutions now

First, I decided to review two of the most popular data mocking frameworks — NSubstitute and Moq. They are pretty similar to each other.

Dependencies references in NSubstitute
Dependencies references in Moq

Upon closer inspection, it’s easily noticeable that both frameworks use the same library — Castle.Core (even the version is the same🙂).

After delving into the details, you can find that Castle.DynamicProxy is utilized in both of the packages. Here is the link to its GitHub repository.

After browsing the documentation a bit, it was discovered that this magical assembly allows intercepting the execution of a method using interceptor implementations, enabling us to capture the behavior of the original method and return the desired result (of course, this is not the only capability of the library, but this is what interests me).

Let’s move on to writing the code!

In simple terms, we wrap a Func delegate in an Expression to analyze and manipulate the code inside the lambda expression during execution.

This allows us to extract information such as the method name, argument types, and the body of the lambda expression.

After obtaining all the useful information about the method (specifically the name and parameters), we store this information in a dictionary and add the value we want to return when the method is called. This is how we create a mock method.

In a similar dictionary, we keep track of how many times the method was called (number of invocations — 0 initially).

Now, let’s write an interceptor that will intercept the method’s execution and perform the necessary actions for information preservation:

To achieve this, we will implement an interface from the Castle.DynamicProxy library. In the constructor, we will pass references to the dictionaries that store the preserved information. In this method, the invocation parameter encapsulates the call to the proxy method.

Next, we’ll check if we have information about this method (which we obtained during the ConfigureMethod call). If we do, we’ll set the return value by retrieving it from the pre-recorded data.

We will also increment the count of method invocations by 1.

Let’s move on to the constructor of our mock:

Here, we create the following points:

  1. generator: An instance of the proxy generator provided by DynamicProxy.
  2. mockInterceptor: An instance of the interceptor class we created earlier. The interceptor is responsible for intercepting method calls on the proxy object and performing additional actions or changing behavior.
  3. As for CreateInterfaceProxyWithoutTarget, this method creates a proxy object that intercepts calls to the interface TInterface. The proxy object is generated at runtime using mockInterceptor.

Now, let’s move on to writing unit tests using the newly created library!

Indeed, we now have a syntax that is intuitive and straightforward and a complete test pass with verification of the library’s functionality.

The principle of operation boils down to preserving the value we want to mock and intercepting the method’s execution using Castle.Core. Instead of calling the original method, we return the preserved value.

DynamicProxy allows for the implementation of countless features, such as logging method calls or extending the functionality of an old and unsupported library.

Of course, this is just a small prototype and not intended for production use. However, after completing this project, the mystery of writing mocks has disappeared.

By using this approach, you have gained a better understanding of how mocking works, and it can be a valuable skill for unit testing and building more robust and maintainable applications.

Well done on completing the project! If you have any more questions or need further assistance, feel free to ask. Happy coding!

Curious to join us on the mission to empower lodging businesses worldwide? Check out our career page for open positions!

--

--