Unit Testing in Sitecore

Mar 31, 2020
Prasanth Nittala

At Oshyn, we believe in the power of unit testing to reduce downstream defects and enforce code quality with the goal of unlocking rapid and frequent releases of Sitecore-based websites. Doing unit testing in Sitecore has traditionally been difficult, time consuming, and mostly left out of Sitecore website implementations. It isn’t appropriate in every situation for Sitecore development, but where it is, it can help make sure your site does not have critical regressions in functionality during every release.

This post covers some of the common patterns you can use to write proper unit tests in Sitecore website/.NET application.

  1. Extract dependency objects and inject them. For example, System.DateTime.Now below.
  2. Isolate Sitecore dependencies to separate object and inject that object similar to #1.
  3. Derive an object from subject under test with overridden methods.
  4. Static method with dependencies that cannot be mocked.

Lets consider the following example to showcase #1, #2 above.

Initial Code to unit test:

public class FolderNameGenerator
{
    public string GetFolderName()
    {  
        //some code
        DateTime currentDate = System.DateTime.Now;
        String folderName = currentDate.ToString(“mmddyyyy”);
        //some  code
        Return folderName;
    }
}

The above method can be changed to have signature of GetFolderName(DateTime currentDate). However, that just pushes the date object to another level and creates issues testing that object. The ideal way to get across is to isolate this dependency.

Above is sample method that generates foldername based on current date. You will not be able to write unit tests for this due to the variable nature of System.DateTime.Now call. Here is how you need to change it:

  • Create an Interface IDate that provides GetCurrentDate() method.
  • Create class TimeImplementation, which implements the above interface IDate and provides for current date.
  • Inject that interface to the FolderNameGenerator to be able to invoke the dependency.

This is a general pattern for isolating the dependencies and injecting them into our objects and use them.

public interface IDate{
    public DateTime GetCurrentDate();
}
public class DateImplementation : IDate
{
    Public virtual DateTime GetCurretDate()
    {
        Return System.DateTime.Now;
    }
}

Now you inject this dependency through constructor injection as shown below:

public class FolderNameGenerator
{
    private readonly IDate _DateDependency;
    public FolderNameGenerator(IDate dateDep)
    {
        _DateDependency = dateDep;
    }
    public string GetFolderName ()
    {
        DateTime currentDate = _DateDependency.GetCurrentDate();
        String folderName = currentDate.ToString(“mmddyyyy”)
    }
}

Now, with the code refactored as shown above, we can easily write unit tests for this method. We can write unit tests as shown in approach #1 and approach #2.

Approach #1: Using Fake Implementation to test

public class FakeDateImplementation: IDate{
    public DateTime GetCurrentDate(){
        Return new DateTime(2020,2,2);
}
}
public class FoldernameGeneratorTests
{
    [Fact]
    public void GetFolderName_Returns_02022020()
    {
        FolderNameGenerator fng = new FolderNameGenerator(new FakeDateImplementation());
        String actual = fng.GetFolderName ();
        Assert.Equal(“02022020”, actual);      
        //Assert for other indicators
    }
}

Approach #2: Using Moq or similar library for mocking and testing.

public class FolderNameGeneratorTests
{
    Mock<IDate> dateDepMock  = new Mock<IDate>();
    dateDepMock.Setup(x => x.GetCurrentDate()).Returns(new DateTime(2020,2,2))
    FolderNameGenerator fng = new FolderNameGenerator(dateDepMock.Object);
    Var result = fng.GetFolderName();
    Assert.Equal(“02022020”, result);
    //assert for other indicators.
}

However, for this to work, you have to mark the method virtual in IDate interface.

This approach generally works for the place where you have to identify a dependency and inject that dependency.

Approach #3: Using Derived object to override some of the methods and testing the method in isolation.

To demonstrate this approach, lets look at the following scenario. Suppose the method to be tested and its calling methods have protected access level. Then this approach will provide for the way to unit test such protected methods. Notice that those methods were marked “virtual” for MethodB(), MethodC() to support overriding, and facilitate the unit test of MethodA().

public class A
{
    protected T MethodA()
    {
        //some code with logic
        var resultB = MethodB(); //call MethodB()
        //some more code
        Var resultC = MethodC(); //call MethodC()
        //some more code
    }
 
    protected virtual T MethodB()
    {
    }
 
    protected virtual T MethodC()
    {
    }
 
    protected T MethodD()
    {
    }
}

As you can see above, methodB and methodC are marked virtual, which allows us to override these methods in derived class. For this, you will create a derived class that inherits “A” class and provides for overridden methods.

public class FakeA : A
{
    protected override T MethodB()
    {
        //some overridden code
    }
 
    protected override T MethodC()
    {
        //some overriden code
    }
}

Now you can write your test as shown below:

public class ATests()
{
    [Fact]
    public void TestMethodA()
    {
        FakeA fakeA = new FakeA();
        var result = fakeA.MethodA();
        //Assert statements for expected vs actual
    }
}

Approach #4:Handling static methods

Static methods with dependencies that cannot be mocked cause issues with unit tests. For example, a static method that calls for File operations, database operations which make it challenging to write unit tests. In such scenarios this below method can be applied. See below methodB is static method in class B and is invoked from A.MethodA().

public class A
{
    public void MethodA()
    {
        B.MethodB();
    }
}
public class B
{
    Public static void MethodB()
    {
        //dosomething
    }
}

For this scenario, we first create an interface and isolate that static method call as shown below:

public interface IB{
    public void MethodC();
}
public class WrapperB:IB
{
    public void MethodC()
    {
        B.MethodB();
    }
}

As shown above, we implement an interface and isolate that static method call. Now we need to inject the interface into class “A” as shown below:

public class A{
    private readonly IB _IB;
    public A():this(new WrapperB())
    {
    }
 
    public A(IB b)
    {
        _IB = b;
    }
     
    public void MethodA()
    {
        _IB.MethodC();
    }
}

This way you can convert a static method by using wrapper and dependency injection and be able to test this. If you use TypeMock, then it handles static methods.

In earlier versions of Sitecore, we used FakeDB for writing unit tests. But with the latest abstractions in Sitecore, it is no longer needed. Unit tests written in this way for Sitecore websites are fast to execute and are run by each developer prior to merge-requests to the develop branch. They are also executed during the build of the software in the CI environment to ensure no broken builds get out to higher environments. This allows us to control our quality on our projects much more tightly and allows the business to deploy new features with confidence much more frequently than they can without the unit tests.