📜 ⬆️ ⬇️

Moles - Isolation Framework from Microsoft Research or how to make unit testing more convenient

Sometimes it happens that a coherent and thought-out system of unit tests is stifled by the strong coherence of components - especially with this legacy code that was not intended for testing suffers. Refactoring, of course, saves - but it is not always possible to refactor. One of the problems arising from the creation of unit tests can be the use of static methods or non-virtual methods that must be overloaded to successfully write tests. This will help in this project from Microsoft Research - Moles.

First, consider an example (it is intentionally simplified) - there is a class PaymentsCore which contains the method AcceptPayment - registering the payment from the user. If the payment is not possible due to lack of funds - the method throws an exception. The task is to write a unit test that will check the functionality of the method. At the entrance to the method are served - user ID and payment amount. This is where the trick lies - user data corresponding to the transferred ID is obtained using the static method of a certain class CacheManager (which accesses the database, puts an object into the application cache, updates the cache depending on conditions - etc.). Yes, one can argue that the architecture is not quite right built - but we will assume that this is legacy code (it is possible that the source code of CacheManager is not at all available). Obviously, to get rid of the cache, the database, depending on the data will help mock-object. However, until recently, the only mock framework that allowed overlapping static methods was the TypeMock Isolator (an expensive commercial product). We will consider how to apply Microsoft Research Moles.
To begin with, we will create a new test project, add a link to the assembly with the application, and write the simplest test for the AcceptPayment method.
[TestMethod]
[ExpectedException( typeof (AmountException))]
public void AcceptPaymentOverdraftTest()
{
PaymentsApp.PaymentsCore core = new PaymentsCore();
core.AcceptPayment(1, 200);
}


The test is performed if there is a client in the database with an ID of 1, if the amount of funds in his account does not exceed 200 - the test is tied to a number of external parameters - and, by and large, it is useless. It is time to mock up the necessary static methods and create the perfect environment for the test.
Download Moles (be careful, this is the x86 version, for x64 - the link is in the Description) and install it. Back in Visual Studio - time to write code.
So the first step is to add a configuration for Moles to the project. To the test project we add a New item with the type Moles and stubs for testing, with the name set as PaymentsApp.moles. File contents will be quite simple.
<? xml version ="1.0" encoding ="utf-8" ? >
< Moles xmlns ="http://schemas.microsoft.com/moles/2010/" >
< Assembly Name ="PaymentsApp" />
</ Moles >

Here are the assemblies for which the stubs will be generated. In Solution Explorer you can also see all generated files, all assemblies will be automatically added to References.

Now you can go directly to the second stage - writing plugs. Returning to the code of the test method and add at the beginning:
using PaymentsApp.Moles;

This namespace is in an assembly generated by configuration and contains for each class of the original assembly a special proxy class - starting with the prefix M. That is, The CacheManager class of interest to us (which contains the cunning static methods) will be called MCacheManager. For methods, properties with the name <originalName> <Parameter1 type> <Parameter2 type> will be generated ... Thus, the property of interest to us is GetClientInt32. The property is of type Func <int, Client> - accepts a delegate, which will be executed instead of the original CacheManager.GetClient method.
So, let's add the line to the beginning of the test method:
MCacheManager.GetClientInt32 = id => new Client( "Test" , 100);

However, it is still too early to run the test - you need to specify the HostType attribute for this method to allow the use of Moles within the method.
[HostType( "Moles" )]

So, the final form of the test method:
[TestMethod]
[HostType( "Moles" )]
[ExpectedException( typeof (AmountException))]
public void AcceptPaymentOverdraftTest()
{
MCacheManager.GetClientInt32 = id => new Client( "Test" , 100);
PaymentsApp.PaymentsCore core = new PaymentsCore();
core.AcceptPayment(1, 200);
}

Now, instead of the static method of the CacheManager class, our delegate will be executed returning the desired mock object. Those. The test is now free from database dependency, cache, etc. - and even without code modifications.

References:
1) Moles - Isolation framework for .NET
2) Video - quick start with Moles (5 min)
3) TypeMock Isolator
4) Source code of the demonstration project

')

Source: https://habr.com/ru/post/98571/


All Articles