In this tutorial, you will learn about dependency injection within Episerver. You will learn about a technique that may help you write cleaner code. You will also helpfully learn a little bit about the CMS and how it has improved over the years.
Trying to get 100% test coverage in your project is a noble goal. In Episerver 6 trying to get good test coverage was difficult. The main way to access page data was via the DataFactory API. Trying to test code that used DataFactory
was difficult. The DataFactory
was implemented using a static helper that followed the singleton pattern. The DataFactory
was difficult to mock. The DataFactory
also relied on the HTTP context to exist. Without the correct context, the DataFactory
would throw a NullReference
whenever it was accessed within a test.
The key to writing tests is to inject all the dependencies required by a class instead of instantiating them directly. In simple terms, you can not use the new
keyword anywhere in your class. Instead, you will need to inject all your dependencies through the constructor as a parameter. This is why the way in which you access the APIs changed within Episerver 7. This change in API access makes for more flexible architectures. Two APIs that come with Episerver 7 are the IContentRepository
and the ILinkResolver
. You can access ILinkResolver
like this:
This code uses a service locator to get access to the API. Service location is considered a bad technique. Instead, you should access an API using constructor injection, like so:
When you start injecting dependencies like this, sooner or later you will run into the problem of injecting too many items.
One way to elevate this parameter overload is to group related dependencies using the facade pattern. In this example, I will create a facade called EpiserverDependencies
. My class will now look like this:
What this code is doing is passing in an 'IContentRepositoryFactory' and 'ILinkResolverFactory'. These are two custom factories that I have created that will create the Episerver dependencies. I also use Lazy
to store the IContentRepository
and ILinkResolver
. If you have never come across Lazy
it allows you to only instantiate something the first time it is actually used. As this class will be passed into every base controller (read on for explanation), we do not want to instantiate everything on every request. The dependencies might not be used on each page request so delaying the load saves processing time and improves performance.
Now we have a class that will make it possible to unit test our code simpler. The next question is, how do we actually this class within a website? The first thing you need to do is inject your dependencies into your website. Episerver comes with Structure Map out-of-the-box. THe code to do this, would look something like this:
Now we have set up Structure Map to inject our base dependencies whenever a call to IEpiserverDependencies
is made. Our next step is to actually start injecting this dependency into a class so we can use it. The starting point for all of your pages will be a controller. In Episerver, one way of structuring your website is to use a base page controller. This allows you to add some common features like your base dependencies and SSL. Below shows you an example of an Episerver base controller:
The next step in this process is to create a corresponding page controller for a page type. In this example, I am going to use an example page called Content
:
In the controller, a view model is created and passed down into the view. Some people get confused around the use of a view model when they have the Episerver page object. In my simple opinion, a controller is a place that the routing engine looks for when it's trying to resolve a URL. The controller is responsible for getting the data required by the view, adding caching, authorization, handling query string. The Episerver page object should not contain code that renders presentation data. Data required in the view should be done in a view model. This creates a clean separation of concerns. Now we have that cleared up, let's see what the view model looks like:
A lot of Episoerver sites use a base view model, where an instance of the current page and Episerver dependencies is passed into it to save on code duplication:
We have our dependencies injected into a view model, let us use them in a view:
This is a very small example, however, you can see how it has removed the dependency from Episerver in the controller. If you wanted to unit test this simple method you can now use something like MOQ to mock IContentRepository
.
That wraps up the first in this series of how to unit tests in Episerver and as of yet, we have yet to write a single test! We have written a mini-framework that will allow you to unit test anything Episerver API used in our code easily and consistently. This is a huge step forward. In the next chapter of this Epi saga, I will show you how you can write tests using fakes or the more popular mocks. The code for the whole of this series can be found on my #ILoveEpiserver project here enjoy :) Happy Coding 🤘