In this post, you will learn about creating fake objects to help you unit test your Episerver CMS powered website. This is the third post in my unit testing series. The aim of this series is to spread knowledge on how to streamline the process of getting good test coverage in your Episerver project 🧠.
In the first post, I talked about injecting a single utility class whose sole purpose is to give you easier access to all the Episerver APIs within your code. Creating this single dependencies factory will not only make your constructors a lot cleaner, but it will also mean you have less code to set up in your unit tests. In the second post, we created some basic unit tests using Moq to mock the dependencies accessed via this factory, making previously un-testable code testable. If you have never heard about a fake then the concept is very easy... it's a class (rather than a mock) that implements an interface you want to test. Its sole purpose is to be used in your test project. The questions I get asked a lot is why use fakes at all, why not use mocks, or, when should I use which method?
In general, I recommend mocking as opposed to fakes. Mocking allows for cleaner and easier to maintain code. The reason for using the main reason I would use a fake is if something has a protected or abstract method making it unmockable. You generally need to create fakes when you integrate with third-party code that Moq does not like. Take this example:
If you run this bit of code (from the example code provided at the end of this post) Moq will throw an exception:
This error happens because the IContentRepository
is not virtual, it cannot be mocked. One way to get around this issue is to use the wrapper/adaptor pattern. Adding lots of wrappers as an intermediary layer for lots and lots of third-party code is often not ideal. If you try to unit test Episerver Commerce for example you will hit this issue a lot. When you hit a testing wall like this, you can choose wrappers or fakes to help you move forward.
In this example, I'm using a custom class that I created called EpiDependencies
. EpiDependencies
inherits from IEpiDependencies
. What we can now do is create a FakeEpiDependencies
object that inherits from IEpiDependencies
and make the ContentRepository
repository virtual, allowing us to mock the function we want to.
We now have a fake test object that we can use in any test where IEpiDependencies
needs to be injected into the constructor. This method is now virtual
meaning it can be mocked. Re-executing the test I detailed at the start of this post, this time using FakeDependencies
instead, our test will now succeed 🔥🔥🔥
Swapping a mock for a fake might seem trivial and obvious, however, this technique can be a massive help in getting full test coverage in your application. In many of Episervers API's (commerce is a much worse offender) you might want to test a function like adding a billing address to a customer and you can't change the underlining API code to make it testable.
If something can't be mocked it can usually be faked. Create a fake object, and override the methods and properties that cant be mocked. You can even use something like AutoFixture to return some dummy data from within these overridden methods. Following this process, my unit test now looks like this:
Another good reason to consider using a fake is to cut down on the amount of set-up test code that you need to create. Take our Dependencies
class. If you need to pass the same dependency into all your view models, blocks, controllers, fixtures etc.. writing the same mocked set-up code over and over is a waste. Having to follow this pattern is a sign of a code smell, however, sometimes it can not be avoided.
You could solve this problem in two ways. First, you could create a base class/factory whose job is to set up the mocks. A simpler approach would be to create a FakeDepencies
class and then use Moq inside FakeDepencies
to return the appropriate mocked dependencies. Using this approach avoids having to create the factory, so one less class to manage!
If you end up creating an Episerver Commerce dependencies manager, which has a lot of API's and dependencies that can not be mocked, this trick will save you time having to mock the whole thing up each time. The code for the whole of this series can be found from my Github profile, here. Happy Coding 🤘