Moles - Isolation Framework from Microsoft Research or how to make unit testing more convenient
Sometimes it happens that a harmonious and well-thought-out system of unit tests is stifled by the strong connectivity of the components - this is especially true for legacy code that was not originally intended for testing. Refactoring, of course, saves - but it is not always possible to refactor. One of the problems encountered when creating unit tests can be the use of static methods or non-virtual methods, which must be overloaded to successfully write tests. Help with this project from Microsoft Research - Moles.
To begin with, consider an example (it is intentionally simplified) - there is a PaymentsCore class that contains the AcceptPayment method - registering a payment from a user. If payment is not possible due to lack of funds, the method throws an exception. The task is to write a unit test that will test the functionality of the method. The method is submitted to the input - user ID and payment amount. This is where the catch lies - user data corresponding to the transmitted ID is obtained using the static method of a certain CacheManager class (which accesses the database, places the object in the application cache, updates the cache depending on conditions, etc.). Yes, we can argue that the architecture is not completely built - but we assume that this is legacy code (it is possible that the source code of CacheManager is not available at all). Obviously, to get rid of the cache, the database, Depending on the data, a mock object will help. However, until recently, the only mock framework that allowed overriding static methods was TypeMock Isolator (an expensive commercial product). We’ll look at how to apply Microsoft Research Moles.
First, create a new test project, add a link to the assembly with the application, and write the simplest test for the AcceptPayment method.
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 many external parameters - and, by and large, it is useless. It is time to mock 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 Description) and install. Returning to Visual Studio - time to write code.
So, the first step is to add configuration for Moles to the project. Add a New item with the type Moles and stubs for testing to the test project, specify PaymentsApp.moles as the name. The contents of the file will be quite simple.
Here are the assemblies for which stubs will be generated. In Solution Explorer, you can also see all the generated files, all assemblies will be automatically added to References.
Now you can go directly to the second stage - writing stubs. We return to the code of the test method and add at the beginning:
This namespace is located in the 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 insidious 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, add the line to the beginning of the test method:
However, it is too early to run the test - you need to specify the HostType attribute for this method to enable the use of Moles within the method.
So, the final form of the test method:
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.
Links:
1) Moles - Isolation framework for .NET
2) Video - quick start with Moles (5 min)
3) TypeMock Isolator
4) Source code of the demo project
To begin with, consider an example (it is intentionally simplified) - there is a PaymentsCore class that contains the AcceptPayment method - registering a payment from a user. If payment is not possible due to lack of funds, the method throws an exception. The task is to write a unit test that will test the functionality of the method. The method is submitted to the input - user ID and payment amount. This is where the catch lies - user data corresponding to the transmitted ID is obtained using the static method of a certain CacheManager class (which accesses the database, places the object in the application cache, updates the cache depending on conditions, etc.). Yes, we can argue that the architecture is not completely built - but we assume that this is legacy code (it is possible that the source code of CacheManager is not available at all). Obviously, to get rid of the cache, the database, Depending on the data, a mock object will help. However, until recently, the only mock framework that allowed overriding static methods was TypeMock Isolator (an expensive commercial product). We’ll look at how to apply Microsoft Research Moles.
First, 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 many external parameters - and, by and large, it is useless. It is time to mock 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 Description) and install. Returning to Visual Studio - time to write code.
So, the first step is to add configuration for Moles to the project. Add a New item with the type Moles and stubs for testing to the test project, specify PaymentsApp.moles as the name. The contents of the file 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 stubs will be generated. In Solution Explorer, you can also see all the generated files, all assemblies will be automatically added to References.
Now you can go directly to the second stage - writing stubs. We return to the code of the test method and add at the beginning:
using PaymentsApp.Moles;
This namespace is located in the 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 insidious 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, add the line to the beginning of the test method:
MCacheManager.GetClientInt32 = id => new Client("Test", 100);
However, it is too early to run the test - you need to specify the HostType attribute for this method to enable 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.
Links:
1) Moles - Isolation framework for .NET
2) Video - quick start with Moles (5 min)
3) TypeMock Isolator
4) Source code of the demo project