Sitecore Unit Testing Guide — Part 4 : Mocking ServiceLocator

Ehsan
4 min readJun 12, 2022

--

Find the source code here : https://github.com/ehsanaslani/SitecoreUnitTesting

In the previous parts we reviews mocking Items, Databases, Sitecore services such as LinkManager and how to write cleaner tests using Fixture.

In the previous article, it was advised to avoid using the Sitecore static classes such as Sitecore.Links.LinkManager and use the dependency injection to make your code testable. But it’s not always easy or possible to do that. For example:

  • You have so many dependencies in your constructor and you don’t want to add more
  • You are creating a static class to add method extensions and therefore you can’t inject the dependencies through constructor

We can still write unit tests even if those dependencies are not injected through constructor but we need to mock ServiceLocator which could be a bit tricky as we will see. However, in this article I want to introduce a better way for achieving this purpose.

Let’s imagine you have added an extension method for Item to get the url of that item

Here you can’t use the dependency injection to inject BaseLinkManager through constructor because it’s an static class. If you try to write unit test for this class, it will fail because the default LinkManager instance will be null when executing the test and it makes it difficult to test.

Mocking ServiceLocator

Let’s take a look at LinkManager class to see how we can test it. First you will see that Sitecore is using the ServiceLocator to create an instance of LinkManager used by static Sitecore.Links.LinkManager class

So to be able to write a test and inject the mocked LinkManager, we should manipulate ServiceLocator to return a mocked BaseLinkManager. We can write a test in this way to do that:

This test tries to create a custom ServiceCollection instance which stores the registered dependencies. It uses DefaultSitecoreServicesConfigurator class to set the basic Sitecore dependencies and then it changes the registerd dependency for BaseLinkManager to our custom mocked linkManager.

Concurrency Problem

This test works fine as long as it runs in isolation but when running in parallel with other tests, you will see that some tests fail randomly. Why? because ServiceLocator is a static class. Once you set the ServiceProvider in one test, it will affect all other tests running in parallel and they might fail because the dependencies set in this test does not match with what they expect.

So what would be the solution? We need to find a way to set the ServiceProvider of ServiceLocator in the scope of one thread so it doesn’t affect other running parallel threads. But how to do that?

Using Sitecore.Context.Items

One way to achieve this goal is to utilize Sitecore.Context.Items. Sitecore.Context.Items is a ThreadStatic property which means it’s a static property which is not shared among threads. That’s how your Sitecore server handles multiple concurrent requests which they each have their own context item as they are serving different pages.

So we need to write a switcher that uses the Sitecore.Context.Items to store the ServiceCollection instance of each thread without affecting other threads.

So let’s create a Switcher class that can switch ServiceProvider just for the thread that uses it. (You can find the source code for this class in the repository)

Then we create a wrapper class for ServiceProvider that delegates the calls to its internal ServiceProvider instance. I the beginning of running tests we will set the ServiceLocator’s ServiceProvider to this wrapper and we will change it’s internal provider when a switching is requested:

So then all we need is to make sure that before our test runs begin, ServiceLocator uses this ServiceProviderWarpper class. To ensure this we create a base test class with a static constructor to make sure this is executed once and only once:

Then we derive our test class from this base class and use the ServiceProviderSwitcher that we created. So our test would be like:

So we make sure that there can be many tests manipulating ServiceLocator that can run in parallel.

Last words

This was the last part in these series of articles. I have tried to cover the most essential parts and yet there are more to talk about. Please provide me your feedbacks and let me know about your requests if you think there are more things to be discussed.

Enjoy testing!

Find me on :

--

--

Ehsan

I’m a software architect specialized in Sitecore content management solutions