Stefan Forsberg
Jul 13, 2010
  4136
(0 votes)

Design principles and testing – part 5

We’ve been talking about various design principles in the last few posts.  So how does this relate to (unit) testing? In a recent talk I attended by Michael Feathers one of the many good points he made was this:

Design your application in a good way makes it testable. Making your code testable doesn’t necessarily make your design good.

So, let’s look at an example to clarify why this is so. Imagine we have a class that has a public method which in turn uses some private methods to do whatever the method needs. If you want to test those private methods you quickly realize you can’t since they’re private (I know there are techniques and products to get around this but if you’re using them for anything else than testing a large brownfield app you’re doing it wrong). When you notice that you can’t really test your application the way it’s designed right now this should be sort of a warning to you that your design is probably flawed.

Maybe those private methods are actually doing something not really related to your class and you’re violating SRP. Perhaps extracting those methods to a class (which in itself is public and testable) could be the solution. Another alternative would be to just change those private methods to public and go ahead with the testing. Notice here that both these approaches leads to you being able to write your tests but in terms of good design I really wouldn’t recommend alternative number 2.

Remember (and this can be somewhat of a pain in the beginning): testing isn’t and shouldn’t be hard. If it is it usually something else that makes it hard, either your design or perhaps the framework you’re using.

 

Writing some tests for our original code

Let’s look at the original version of our MainBody formatting method.

   1: protected void SaveClick(object sender, EventArgs e)
   2: {
   3:     var newMainBody = NewMainBody.Text
   4:         .Replace(Environment.NewLine, "<br />");
   5:  
   6:     var writablePage = CurrentPage.CreateWritableClone();
   7:  
   8:     writablePage.Property["MainBody"].Value = newMainBody;
   9:  
  10:     DataFactory.Instance.Save(writablePage, SaveAction.Publish);
  11: }

This code was places inside a code behind file that inherits from EPiServers TemplatePage class. Ignoring for the moment what we’ve learned about SRP and OCP and say that we want to test that the class correctly replaces newline with br.

Regardless of what framework you’re using for testing (I’m using NUnit here) the basic flow is more or less the same. You initialize some state (Arrange), you call your methods (Act) and finally verify your expectations (Assert).

So we create a new class library, add a reference to our web project and to the NUnit framework. With that in place we start to write the code and the first “problem” arises somewhere around here

   1: [Test]
   2: public void SaveClick_replaces_NewLine_with_br()
   3: {
   4:     // Arrange            
   5:     var defaultPage = new EPiServer.Templates.Default();
   6: }

It’s hard to tell by the code, but to create an instance of the Default page we need to add some EPiServer references as well as one to System.Web. It's feels slightly odd since we really only want to test some text replacement, but let’s play along for now.

When we want to call the SaveClick method we realize it’s protected so we can’t reach it. Hmm ok, so let’s make it public. Then we realize that we need some sort of… We could continue down this road but it basically just leads to heartaches.

A lot of people (yours truly included) in this situation think something along the lines of “god, testing is so hard and I have to make a lot of changes to my code that doesn’t make sense just to be able to test it”.

 

Writing tests for our refactored code

The case of testing our method that replaces newline with br could look something like this

   1: [TestMethod]
   2: public void MainBodyFormaterNewLine_String_With_NewLine_Returns_Same_String_With_Br()
   3: {
   4:     // Arrange
   5:     var stringWithNewLine = string.Concat("Hello", Environment.NewLine, "World");
   6:     var expectedResult = "Hello<br />World";
   7:     
   8:     // Act
   9:     var result = new MainBodyFormaterNewLine().Format(stringWithNewLine);
  10:  
  11:     // Assert
  12:     Assert.AreEqual(expectedResult, result);
  13: }

Since our method only does one thing it’s very easy to test and we only have to worry about things that concerns the method we want to test.

image

And this is the result when running the test (using ReSharpers testrunner). Pretty ey?

In the next post I’ll give some tips and concrete examples of problematic areas to test and what you can do to get around it.

Jul 13, 2010

Comments

Please login to comment.
Latest blogs
Preview multiple Visitor Groups directly while browsing your Optimizely site

Visitor groups are great - it's an easy way to add personalization towards market segments to your site. But it does come with it's own set of...

Allan Thraen | Sep 26, 2022 | Syndicated blog

The Report Center is finally back in Optimizely CMS 12

With Episerver.CMS.UI 12.12.0 the Report Center is finally re-introduced in the core product.

Tomas Hensrud Gulla | Sep 26, 2022 | Syndicated blog

Dynamic Route in ASP.NET Core When MapDynamicControllerRoute Does Not Work

Background Creating one of the add-on for Optimizely I had to deal with challenge to register dynamically route for the API controller. Dynamic rou...

valdis | Sep 25, 2022 | Syndicated blog

404 Error on Static Assets Within an Optimizely plugin

Background With the move to CMS 12 and .NET 5/6, developers are now able to build Plugins and Extensions using Razor Class Libraries (RCL).  These...

Mark Stott | Sep 23, 2022

How to bypass the content creation view in Optimizely

Something that has come up a couple of times in the last few year is feedback from content editors about the editing view that comes up when creati...

Ynze | Sep 23, 2022 | Syndicated blog

Welcome to Optimizely World's New Tech Video Portal

Optimizely, leader in the digital experience realm, has become a wealth of world class SaaS products including Web Experimentation, Full Stack, B2B...

The Developer Marketing Team of Optimizely | Sep 22, 2022