public

Using Microsoft Fakes to test untestable code

I don't write that much about unit tests. I don't have that much experience with them. But I recently learned how to use shims in the

9 years ago

Latest Post Deferring decisions in Evolutionary Architecture by Tim Sommer public

I don't write that much about unit tests. I don't have that much experience with them. But I recently learned how to use shims in the 'Microsoft Fakes' framework that allows you to easily test code that is almost impossible to test. Without changing the actual code itself.
This was simply to good to ignore :).

Untestable code ?

Not all code you write is easy to test. Maybe none of your code is easy to test, I guess it depends on how you write your code. If you follow the rules defined in SOLID, you should be in the clear. But I find it nearly impossible to always follow these rules.

Let's take a look at the following code fragment:

public decimal CalculateTaxes(decimal price)
{
   if (DateTime.Now.Year <= 2014)
   {
     price = price*1.21M;
   }
   else
   {
     price = price * 1.06M;
   }

   return price;
}

In this example, calculation of taxes depends on the current year. Before 2015 we add 21%, otherwise we add 6%.

Now, I know that you could argue that we should view the DateTime object as a dependency. That way you could test it easily. But let's be honest, how often do you write code that does that? And if for some reason you do have a special year calculator, this example can be applied to each static method you use in a class.

So, how do you test static code like this?

Introducing Microsoft Fakes

Microsoft has already encountered this problem and offers the Fakes framework as a solution. Fair warning though, this is only available if you use Visual Studio Enterprise. I have no idea if you can do the same with other (free) testing frameworks. If you do, please don't hesitate to comment so I can update this post.

Update

Fernando pointed out that there is in fact an open source and free alternative. I have not tested this myself, but it looks pretty solid. You can simply install it from the VS gallery!
Take a look at 'Prig: Open Source Alternative to Microsoft Fakes' here.

So what is Ms Fakes?

Microsoft Fakes help you isolate the code you are testing by replacing other parts of the application with stubs or shims. These are small pieces of code that are under the control of your tests. By isolating your code for testing, you know that if the test fails, the cause is there and not somewhere else.

- MSDN

A bit abstract. What is a shim and how do we use it ?

A shim modifies the compiled code of your application at run time so that instead of making a specified method call, it runs the shim code that your test provides. Shims can be used to replace calls to assemblies that you cannot modify, such .NET assemblies.

- MSDN

Aha! If that's true, we can use shims to modify the compiled 'DateTime.Now.Year' code, returning a testable value. So let's try it out!

Writing our test

Testing the 'CalculateTaxes' method itself is fairly easy:

[TestMethod]
public void Test2014YearTaxCalculation()
{
    var _price = 1256.45M;
    var _taxCalculator = new TaxCalculator();

    var _result = _taxCalculator.CalculateTaxes(_price);
    Assert.AreEqual(_result, _price * 1.21M);
}

This test will always fail, because we are in the year 2015 and taxes are 6%. So let's create a shim.
We start by creating fake assemblies.

Add fake assemblies

Right click the assembly you want to add (in this case we add 'system' which contains DateTime and the assembly 'MsFakes' that contains the code we want to test).

Fake assemblies added

We now have our fake assemblies which we can use to create our test. Let's take a look at the following test, that will now succeed:

[TestMethod]
public void Test2014YearTaxCalculation()
{
    var _fixedYear = 2011;
    var _price = 1256.45M;

    // Shims can be used only in a ShimsContext:
    using (ShimsContext.Create())
    {
       // Shim DateTime.Now to return a fixed date:
       System.Fakes.ShimDateTime.NowGet = () => new DateTime(_fixedYear, 1, 1);

       var _taxCalculator = new TaxCalculator();
       var _result = _taxCalculator.CalculateTaxes(_price);
       Assert.AreEqual(_result, _price * 1.21M);
    } 
}

Awesome !

Tim Sommer

Published 9 years ago

Comments?

Leave us your opinion.