In this tutorial, I will cover some of the challenges that you will encounter if you want to get high test coverage when building a project using Episerver Commerce 7. I will also cover some strategies that you can use to make it happen. This might be a bit of a controversial post for some people. Episerver CMS has made amazing progress in the last few years towards creating a platform that is very easy to unit test. Episerver Commerce is also making baby steps towards this goal, however, it is very far behind compared to CMS ðĪŠ
It is definitely possible to unit test a lot of commerce, the big problem is time. Depending on the level of cover you want to reach, be prepared to spend a lot of extra development resource time wrapping the APIs to make things testable. I'll go over the wrapper/adaptor pattern shortly. You will get to know this pattern VERY well if you want to unit test commerce. To forewarn you now, you'll be writing a lot of extra wrapper code around these areas:
- Cart Helper
- LineItem Collection
- OrderAddress
- PaymentCollection
- Payment
- Cart
- ShipmentCollection
- Shipment
- Customer Contact
In order to wrap all these APIs, be prepared to wrap a few thousand lines of code before you can make everything testable. If you have very tight deadlines, you may want to consider the number of resources you'll need to throw at the problem of making commerce testable. For a finger in the air guess, I would estimate it will add a minimum of an extra two weeks into your project deadlines ð
Before we get started, you will need to understand what the wrapper/adaptor pattern is. A wrapper class is a facade over an existing class, forwarding all of the base classes underlying methods and properties into the class it wraps. The important thing about the wrapper is that you build it with an interface. As the wrapper has an interface, you can use SOLID principles with it. Meaning, you can then test and Moq the wrapper in your tests ðĨðĨðĨ
Using a wrapper means the commerce code that would throw an exception when under test can now be mocked. Using wrappers will mean that you can write tests against code that was previously impossible ðĨ. I'll demonstrate a quick example:
The above code is very brief, however, it will hopefully demonstrate the challenges of testing code not written to SOLID principles. On Line1, we define a class OldClassWithNoInterface
. When you try and call this class under test conditions, it'll throw an exception â. In commerce, a lot of the base class will throw an exception under test. A lot of the constructor code has database and request context dependencies. This means that when you try and check if a property is null
, instead of failing gracefully it throws an exception is generated instead ðą.
If you are 100% committed to testing your code, you can wrap all code where this is possible around a try
/catch
and ignore the Exception
. This is not ideal for production code and not recommended. Alternatively, you can write a wrapper that abstracts the API and puts a facade over it, allowing you to Moq the wrapper in your tests. This is where our new WrapperClass
comes into play.
The wrapper shouldn't have any logic, or do anything. All it should do is forward and retrieve information to and from the base class. Your new wrapper class should implement an interface defining all the methods and properties it's forwarding/retrieving. After creating the wrapper, you need to register it with your IoC container. In Episerver, this is usually StructureMap. With the wrapper registered, whenever you need to use that API, you inject your Wrapper Interface via the constructor to access it. You have now solved your unit testing issues. That previous code that caused your unit test to fail is now mockable. You can now carry on testing your original bit of code without commerce throwing an exception. The process is fairly simple, the problem comes when you need to wrap 40-50 classes. I can confirm this amount of wrapping is needed for Episerver Commerce ð
Wrapping Episerver Commerce Code 101
You now know the pattern, lets see what a real-life Episerver commerce looks like the example. Below I will wrap the Payment
class:
This example only wraps a very small portion of the payment class. It takes in the original payment class type as a constructor object. It then stores it as a private instance variable. The wrapper then exposes the Parent
method. In this method, the wrapper simply forwards on and returns a request to the base Parent
property. This class implements a custom interface called PaymentWrapper
. Let us see what that looks like:
Let us pretend that we have a class called ProcessPayment
that needs to get a payments OrderHistory
. We would inject the wrapper via the constructor so it can be tested like this:
What we're doing here is passing in the wrapper. More importantly, we are passing an interface rather than a concrete type. This means we can swap it out under test easily. In this example, Parent
would throw an exception if it was called directly. We can then Moq and swap the call out to Payment
so our unit test runs as expected ðĨðĨðĨ
By now you should have a basic understanding of how to unit test commerce. You may be thinking, that was a lot of code just to test a single line of code. I very much agree. Unfortunately, because the commerce APIs have not been written with unit testing in mind, the hard work has been pushed to you as an implementor to deal with. What are your thoughts on this process? Have you tested commerce in a different way or have you got stuck trying to test something yourself? Leave your thoughts in the comments section below. Happy Coding ðĪ