EPiServer: How to unit test with all those static methods…

So, I’m working with EPiServer now and I want to unit test my code. It is hard. Very hard. The code base I have is not wrapping any of the EPi dependencies and there isn’t time to re-write the code just for unit testing so I have to figure out another way to test my code. I’ve tried to find good frameworks to mock static methods, but there aren’t any free ones (well, as I’m writing this, I actually found one, so keep on reading).

There are, however, commercial frameworks that does this. One of them is Microsoft Fakes. Now, this is a bit price heavy as it is only available in the VS Premium/VS Ultimate bundle (Premium is about $6000, so it’s really not free) but if you have it available then this is the article for you :).

These are the alternatives I’ve found, even though this article will use Microsoft Fakes:

  • Microsoft Fakes ($6000, with Visual Studio 2012 Premium or above)
  • Just Mock (Telerik, $399)
  • Type Mock ($399)
  • Prig (Free, I just found this when writing the article, I’ll get back to you on this one :P)

 

So, your underlying problem would be these static methods. The following code depicts an example where I want to get carts from EPi Commerce using the OrderContext singleton. I also use the static LocalizationService for getting an error message if I can’t find any carts. Disclaimer: the following code is just an example, returning a Tuple as a result isn’t great and the general standard of the code is abysmal. Take it for what it is, an example.

 

public static class Foo
{
    public static Tuple<string, Cart[]> GetCarts()
    {
        var parameters = new OrderSearchParameters(); //Fill with whatever
        var options = new OrderSearchOptions(); //Fill with whatever
        int recordCount;

        var carts = OrderContext.Current.FindCarts(parameters, options, out recordCount);

        if (recordCount == 0)
        {
            string errorMessage;
            LocalizationService
                .Current
                .TryGetString(
                    "/general_error", out errorMessage);

            return Tuple.Create(errorMessage, new Cart[0]);
        }

        return Tuple.Create(string.Empty, carts);
    }
}

So, we have a static method that returns a Tuple. If something goes wrong the Tuple contains an error message. I don’t expect you to like this code, the point of it is that it has static dependencies and you need to test the method. You may not even have written the code yourself, but you want to test it anyway to find ways to improve it (without wrapping and changing it too much).

To use Microsoft Fakes you must have either Visual Studio 2012 Premium/Ultimate (or probably 2013+). To generate the fake versions of your static methods you need to fake the DLL’s they’re in. You do this by, in your test project, right-clicking on the reference and selecting Add Fake Assembly. This will generate a folder in your project called Fakes in which you will find a .fakes file with the references name first. In this example I need to fake EPiServer.Framework and MediaChase.Commerce so in that folder I now have two files called EPiServer.Framework.fakes and MediaChase.Commerce.fakes.
This gives us Stubs and Shims on everything available in those DLL’s. Read more about the difference between stubs and shims on this link.

Using Microsoft Fakes you can now write tests on the above code like this (notice that all usage of shims must occur inside a ShimsContext):


[TestFixture]
public class FooTest
{
    [Test]
    public void IfCarts_AreFound_ReturnCarts()
    {
        using (ShimsContext.Create())
        {
            //Set the OrderContext singleton up!
            ShimOrderContext.CurrentGet = () => new ShimOrderContext();
            ShimOrderContext
                .AllInstances
                .FindCartsOrderSearchParametersOrderSearchOptionsInt32Out =
                (OrderContext t, OrderSearchParameters parameter, 
                    OrderSearchOptions options, out int recordCount) =>
                {
                    recordCount = 1;

                    //This part is important. FormatterServices allows
                    // for you to create an instance of a class without
                    // the usage of its inherit constructs. The Cart class
                    // has a lot going on during instantiation which
                    // causes problems when you just want a "mock".
                    var deadCart = (Cart)FormatterServices
                        .GetUninitializedObject(typeof(Cart));
                    return new[] { deadCart };
                };

            //Run test
            var resultTuple = Foo.GetCarts();

            //Check the results
            Assert.That(resultTuple.Item1, Is.Empty);
            Assert.That(resultTuple.Item2.Length, Is.EqualTo(1));
        }
    }

    [Test]
    public void IfCarts_AreNotFound_ReturnErrorMessage()
    {
        using (ShimsContext.Create())
        {
            //Set the localization service up!
            const string errorMessage = "An error!";
            ShimLocalizationService.AllInstances
                .TryGetStringStringStringOut =
                (LocalizationService instance, string input, out string output) =>
                {
                    output = errorMessage;
                    return false;
                };

            //Set the OrderContext singleton up!
            ShimOrderContext.CurrentGet = 
                () => new ShimOrderContext();
            ShimOrderContext
                .AllInstances
                .FindCartsOrderSearchParametersOrderSearchOptionsInt32Out =
                (OrderContext t, OrderSearchParameters parameter, 
                    OrderSearchOptions options, out int recordCount) =>
                {
                    recordCount = 0;

                    return new Cart[0];
                };

            //Run test
            var resultTuple = Foo.GetCarts();

            //Check the results
            Assert.That(resultTuple.Item1, Is.EqualTo(errorMessage));
            Assert.That(resultTuple.Item2.Length, Is.EqualTo(0));
        }
    }
}

You have now created a fake (shim) version of you static dependencies. The tests run and you can go back to being happy again!

Additional extra super-duper note: EPiServer contains a whole bunch of classes such as the Cart-class which during instantiation does a whole world of things which requires you to feed it with proper data or have a working database connection or else it crashes horribly. In order to bypass those constructs I’m using the FormatterServices.GetUninitializedObject method which allows for you to create an instance of a class without calling the constructor of that class. It’s brilliant and very useful when unit testing EpiServer.