Stefan Forsberg
Jul 13, 2010
  4611
(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
Creating an Optimizely CMS Addon - Adding an Editor Interface Gadget

In   Part One   of this series, I covered getting started with creating your own AddOn for Optimizely CMS 12. This covered what I consider to be an...

Mark Stott | Aug 30, 2024

Configure your own Search & Navigation timeouts

The main blog Configure your own Search & Navigation timeouts was posted for years but you need copy the code to your application. We now bring tho...

Manh Nguyen | Aug 30, 2024

Joining the Optimizely MVP Program

Andy Blyth has been honoured as an Optimizely MVP, recognising his contributions to the Optimizely community. Learn how this achievement will enhan...

Andy Blyth | Aug 29, 2024 | Syndicated blog

Welcome 2024 Summer OMVPs

Hello, Optimizely community! We are thrilled to announce and welcome the newest members to the Optimizely Most Valuable Professionals (OMVP) progra...

Patrick Lam | Aug 29, 2024

Create custom folder type in Edit Mode

Content Folders, which are located in assets pane gadget or multichannel content gadget, allow you to add any type of block or folders. But...

Grzegorz Wiecheć | Aug 28, 2024 | Syndicated blog

Creating an Optimizely AddOn - Getting Started

When Optimizely CMS 12 was launched in the summer of 2021, I created my first AddOn for Optimizely CMS and talked about some of the lessons I learn...

Mark Stott | Aug 28, 2024