Try our conversational search powered by Generative AI!

Stefan Forsberg
Nov 25, 2010
  7601
(5 votes)

Making EPiServer code testable – some terms and tools

This is a post in a series about testing and EPiServer. This post will cover some basic concepts as well as the tools we’re going to be using later on. I find that discussing naming style of tests, while important, can be as much of a productivity killer as a religious debate about prefix private variables with an underscore.

The point here is to recognize enough of the terms and tools so the following posts are possible to follow at least in theory.

Testing with NUnit

The code we’re going to use as an example is the classic Add method in a calculator class.

   1: public class Calculator
   2: {
   3:     public int Add(int a, int b)
   4:     {
   5:         return a + b;
   6:     }
   7: }

Exiting stuff huh?

So, to write tests for this we’re going to be using NUnit. The easiest way to download NUnit is through NuGet. If you’ve installed it’s as simple as adding a package reference, search for Nunit and download it.

image

You can also get it from their homepage.

Writing tests

There are a few different schools on how to name your tests, how the tests should be written and such. I tend to follow the Arrange, Act, Assert (AAA) pattern.

Given the code above I’d like a test that verifies that our add method is capable of adding to numbers and returning the result. A test like that could look like this:

   1: [TestFixture]
   2:     public class CalculatorTests
   3:     {
   4:         [Test]
   5:         public void Given_two_numbers_when_adding_them_then_the_sum_is_returned()
   6:         {
   7:             // Arrange
   8:             int a = 5;
   9:             int b = 5;
  10:             int expectedResult = 10;
  11:  
  12:             // Act
  13:             var result = new Calculator().Add(a, b);
  14:  
  15:             // Assert
  16:             Assert.That(result, Is.EqualTo(expectedResult));
  17:         }
  18:     }

Unfortunately(? I don’t write too many classes like the calculator class. Especially not in an EPiServer project. More often than not you’re writing classes that have dependencies on other classes. I find that while it’s certainly possible to write unit tests without using mocking frameworks in practice it’s not something I’d recommended.

What the mock?

So when and why do you want to use mocks?

First scenario is that we have some code that’s depending upon our beloved friend HttpContext. In .Net 3.5 SP1 a few new classes were introduced that helps testing various http related classes. One of them is the HttpRequestBase class. HttpRequestBase defines virtual properties for all the properteries from HttpRequest and then you have HttpRequestWrapper that overrides all those properties and delegates them to the actuall HttpRequest object. Let’s say that we have a method that should behave differently depending on the value of a certain querystring.

   1: public CustomerFromQueryStringParser(HttpRequestBase httpRequest)
   2: {
   3:     _httpRequest = httpRequest;
   4: }
   5:  
   6: public string GetCustomerGrade()
   7: {
   8:     var customer = _httpRequest.QueryString["customer"];
   9:  
  10:     if (string.IsNullOrEmpty(customer))
  11:     {
  12:         throw new NoCustomerFoundException("Found no customer");
  13:     }
  14:  
  15:     if(customer.StartsWith("a"))
  16:     {
  17:         return "a customer";
  18:     }
  19:  
  20:     return "b customer";
  21: }

Let’s say that we want to create the following tests

Given a querystring that does not contain a customer a specific exception is thrown

Given a querystring that contains a customer starting with a then a a grade customer is returned

Given a querystring that contains a customer not starting with a then a b grade customer is returned.

So, let’s start with the first test. We need to implement the abstract class HttpRequest and make sure that the QueryString property returns an empty name value collection. For the second test we need to return a customer starting with “a” and so on and so forth. This could look something like this:

   1: public class HttpRequestWithNoCustomerInQueryString : HttpRequestBase
   2:     {
   3:         public override System.Collections.Specialized.NameValueCollection QueryString
   4:         {
   5:             get 
   6:             { 
   7:                  return new NameValueCollection();
   8:             }
   9:         }
  10:     }
   1: public class HttpRequestWithAGradeCustomerInQueryString : HttpRequestBase
   2: {
   3:     public override System.Collections.Specialized.NameValueCollection QueryString
   4:     {
   5:         get
   6:         {
   7:             return new NameValueCollection {{"customer", "ablabla"}};
   8:         }
   9:     }
  10: }

With a test looking like this:

   1: [Test]
   2: public void Given_a_querystring_that_contains_a_customer_starting_with_a_then_a_a_grade_customer_is_returned()
   3: {
   4:     // Arrange
   5:     var parser = new CustomerGradeFromQueryStringParser(new HttpRequestWithAGradeCustomerInQueryString());
   6:  
   7:     // Act and Assert
   8:     var result = parser.GetCustomerGrade();
   9:  
  10:     Assert.That(result, Is.EqualTo("a customer"));
  11: }

As you can imagine the amount of classes you have to create to satisfy the various test scenarios can become numerous. So, let’s see how these mocks can help us out. 

Using Moq

I’m using the mocking framework called Moq. There are other frameworks out there so search around until you find one that suits you.

Again, the simplest way to download Moq is using NuGet but you can also download it from their homepage.

With Moq the basic flow for mocking classes like above is to create a moq of the type you want to mock.

   1: var httpRequestMock = new Moq.Mock<HttpRequestBase>();

You then instruct Moq on what should happen when someone interacts with the mocked object.

   1: httpRequestMock
   2:     .Setup(x => x.QueryString)
   3:     .Returns(new NameValueCollection());

To actually use the object we pass in the Object property of the mocked object. So when we create a new instance of Mock<T> the resulting type is a Mock of T and the property Object is of type T.

   1: var parser = new CustomerGradeFromQueryStringParser(httpRequestMock.Object);

So instead of creating tailored classes we create the behaviors we want on the fly.

What can you mock?

Moq acts as a proxy around your mocked type so it needs to be able to override the member you setup. So, abstract or virtual members makes Moq happy. The virtual part has been something of a hot potato in .Net in general. As I’m sure you’re aware virtual is default in Java but not in .Net. Making more members virtual in the EpiServer code base would surely improve testability so I’ll just leave this message from “The virtual boys” (and yes, being able to lean your head like that is a requirement for joining “The virtual boys”).

image

 

Interaction(s)

Another usage scenario is when you want to verify that objects interacts with each other in a specific way.

Let’s say we have some code that changes the name of a PageData object according to some business rule and then saves it. Since you do your best of following the SOLID principles you have one class that changes the name (and nothing more) and then a façade that used that class as well as saving the page through the data factory.

   1: public class SomeFacade
   2:     {
   3:         private readonly IDataFactoryFacade _facade;
   4:         private readonly SomePageNameThing _pageNameHandler;
   5:  
   6:         public SomeFacade(IDataFactoryFacade facade, SomePageNameThing pageNameHandler)
   7:         {
   8:             _facade = facade;
   9:             _pageNameHandler = pageNameHandler;
  10:         }
  11:  
  12:         public void Execute(PageData pageData)
  13:         {
  14:             if(_pageNameHandler.ShouldChangeName(pageData))
  15:             {
  16:                 pageData.PageName = _pageNameHandler.GenerateNewName(pageData);
  17:                 _facade.Save(pageData, SaveAction.Publish);
  18:  
  19:             }
  20:         }
  21:     }

The important part here is that you do not care what the Save method does, you only want to make sure that it is being called. Likewise you don’t care about the logic performed in the ShouldChangeName and the GenerateNewName methods.

This is somewhat of a pattern in how I work nowadays. Write small, specific classes with accompanying tests and then facades with tests for the integration of those classes it depend on. In this case we would have tests for SomePageThing that verifies that the methods ShouldChangeName and GenerateNewName do what they are supposed to do but in the context of testing the façade class we really don’t care. What we do care about is that if the ShouldChangeName method returns true it should set the PageName on the page data object and then save it.

Let’s walk though this step by step.

   1: [Test]
   2: public void Given_a_page_that_should_have_its_name_changed_when_executing_the_page_name_is_set_to_the_new_generated_name()
   3: {
   4:     // Arrange
   5:  
   6:     // Act
   7:  
   8:     // Assert
   9: }

We create a PageData object and the mocked objects we need as well as the facade itself.

   1: // Arrange
   2: var dataFactoryFacadeMock = new Mock<IDataFactoryFacade>();
   3: var somePageNameThingMock = new Mock<SomePageNameThing>();
   4:  
   5: var pageData = new PageData();
   6: pageData.Property.Add("PageName", new PropertyString("Old name"));
   7:  
   8: var someFacade = new SomeFacade(dataFactoryFacadeMock.Object, somePageNameThingMock.Object);

Next we need to setup out SomePageNameThing mock so that it behaves the way we need to for this this. First of we need to make sure that the call to ShouldChangeName returns true. We must also make sure that the call to GenerateNewName returns some string.

   1: somePageNameThingMock
   2:     .Setup(x => x.ShouldChangeName(pageData))
   3:     .Returns(true);
   4:  
   5: somePageNameThingMock
   6:     .Setup(x => x.GenerateNewName(pageData))
   7:     .Returns("New name");

To finish we need to write our act and assert.

   1: // Act
   2: someFacade.Execute(pageData);
   3:  
   4: // Assert
   5: Assert.That(pageData.PageName, Is.EqualTo("New name"));

In much the same way we can verify that the save method is being called on the datafactory façade.

   1: [Test]
   2: public void Given_a_page_that_should_have_its_name_changed_when_executing_the_page_should_be_saved_and_published()
   3: {
   4:     // Arrange
   5:     var pageData = new PageData();
   6:     pageData.Property.Add("PageName", new PropertyString("Old name"));
   7:  
   8:     var dataFactoryFacadeMock = new Mock<IDataFactoryFacade>();
   9:  
  10:     var somePageNameThingMock = new Mock<SomePageNameThing>();
  11:     somePageNameThingMock
  12:         .Setup(x => x.ShouldChangeName(pageData))
  13:         .Returns(true);
  14:  
  15:     somePageNameThingMock
  16:         .Setup(x => x.GenerateNewName(pageData))
  17:         .Returns("New name");
  18:  
  19:     var someFacade = new SomeFacade(dataFactoryFacadeMock.Object, somePageNameThingMock.Object);
  20:  
  21:     // Act
  22:     someFacade.Execute(pageData);
  23:  
  24:     // Assert
  25:     dataFactoryFacadeMock.Verify(x => x.Save(pageData, SaveAction.Publish));
  26: }

Note that we use the Verify method on the moq to do this verification. As you can imagine being able to mock various EPiServer classes like this makes it so much easier to test scenarios where you need to interact with EPiServer in one way or another.

Next post…

So hopefully this post will make the following discussion about hot to make EPiServer code testable will make more sense. The next post will focus on the steps taken to test the fallback language functionality.

Nov 25, 2010

Comments

Nov 25, 2010 12:43 PM

Thanks for sharing!

Please login to comment.
Latest blogs
Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog

The A/A Test: What You Need to Know

Sure, we all know what an A/B test can do. But what is an A/A test? How is it different? With an A/B test, we know that we can take a webpage (our...

Lindsey Rogers | Apr 15, 2024

.Net Core Timezone ID's Windows vs Linux

Hey all, First post here and I would like to talk about Timezone ID's and How Windows and Linux systems use different IDs. We currently run a .NET...

sheider | Apr 15, 2024

What's new in Language Manager 5.3.0

In Language Manager (LM) version 5.2.0, we added an option in appsettings.json called TranslateOrCopyContentAreaChildrenBlockForTypes . It does...

Quoc Anh Nguyen | Apr 15, 2024