codeflood logo

Testable Code

Ahhh, automated testing. It's what gives you that warm fuzzy feeling that your code is all working as you intended. A validation that you're free from errors and a safety net for future modifications. OK, perhaps not necessarily free from errors, but you at least know the cases you've got covered are correct, and they're easy to run at any time to validate those cases are still working as expected. When developing code, we'd like to be able to write tests for it with minimal fuss. After all, if the tests are hard to write there's a chance they won't get written. Or only a small number will get written which doesn't cover enough of the problem space to properly validate correct behaviour. This post contains a couple of guidelines for helping you write code that can be tested easily.

Design for Testability

Testability is a design concern. Design occurs at many levels on any project, and testability can be designed in at every level. But the design level that makes the most impact to the testability of a piece of code is the design done during construction. Although the higher levels of design can have a bearing, nothing will save you if the design during construction ignores testability as a requirement. The thing is, code that is well designed is generally testable. Passing the right parameters, making sure classes are drawn up at the correct scope, communication between classes, etc. All these principals contribute to making it easier to test your code. A design that is abstracted well will yield code that is easily testable. Not just that, but pinpointing where an error has crept in will be much easier. Abstraction is one of the design aspects that is done at all levels of design. Even if the higher level of design didn't quite get the abstractions right, the developer can still abstract concerns during construction to make this more easily testable. Let's start with a common issue in ASP.NET development, putting domain level logic into your request handlers. A request handler is the bit of code that runs to handle a request. That includes the code-behind for a WebForm, controllers, web service endpoints, etc. Don't place domain logic directly in those classes. Instead that logic should be put into separate classes which can be easily instantiated outside of a web context and easily orchestrated. The request handler should only deal with marshalling and parsing of data. This makes the domain level logic much easier to test, plus the added bonus or being able to reuse that logic in other locations within your application. This is also an example of where design during construction can help with testability. The high level design may not require the domain logic be extracted to a separate class but will likely list the request handlers that should be implemented. The developer can extend the high level design to include the domain level objects separate (and testable!) from the request handler. But we don't need to stop at just extracting appropriate domain level classes. Appropriate abstraction inside a class and within the methods can make testing of a complex class much easier, and can even help make a semi-deterministic process more predictable. If a process is somewhat unpredictable, split the process into separate methods, constraining the unpredictable stuff to a single method, reducing the scope of the unpredictable region. And if you make that method virtual, then it can be replaced through a mock or dummy class for the test. Before I jump into an example, you might be wondering about how code can be unpredictable. As soon as we step into the realm of multi-threading, we can have unpredictable outcomes such as race conditions. The ordering here is unpredictable. Or perhaps your class does it's work on a background thread so as not to block the primary execution. Trying to catch that thread in a test to verify the result of the operation is a nightmare. So let's take a look at how we can extract difficult-to-test code to a separate method to make testing easier. C#

public class JobRunner
{
  private ConcurrentQueue queue = null;

  public JobRunner(ConcurrentQueue queue)
  {
    this.queue = queue;
  }

  public bool ProcessQueue()
  {
    var threadCount = 4;
    for(var i = 0; i < threadCount; i++)
    {
      StartThread();
    }
  }

  protected virtual StartThread()
  {
    // launch a thread and pass the queue to it
  }
}

By extracting the thread creation part to a separate virtual method we can override that in a dummy class to be used inside a test. Rather than spawning the thread when testing we can execute sequentially, allowing the result to be captued and inspected. C#

public class DummyJobRunner
{
  public int SomeMetric { get; private set; }

  protected override StartThread()
  {
    SomeMetric++;
  }
}

Many mocking frameworks also provide this kind of orchestration so you don't have to dummy up the classes yourself. Now we won't be launching new threads that we need to try and track and the test becomes deterministic. By exposing some of the internals of the class to the test we can validate it's working properly.

Dependency Injection

You'll often hear Dependency Injection (DI) used in the same conversations as Inversion of Control (IoC). And although DI is required to make good and proper use of IoC, DI has more uses than just IoC. Dependency Injection is a design pattern where developers pass in the dependencies a class requires rather than making the class find those dependencies itself. The benefit for testing is that in a testing scenario one can provide the dependencies to the class, whether they be real implementation, dummy classes or Mocks. It makes for easier test setup code. DI comes in two flavours: Constructor Injection and Parameter Injection. In Constructor Injection the dependencies are provided as parameters to the constructor, whereas with Parameter Injection the dependencies are set on the parameters. And of course you can mix the approaches as well. To determine which approach one should use, just determine whether the dependency is a requirement of the class. If the class can't live without the dependency then it should be provided in the constructor. Here's an example of using Constructor Injection in a class. C#

public class ItemLocator
{
  private readonly ISearcher searcher = null;

  public ItemLocator(ISearcher searcher)
  {
    this.searcher = searcher;
  }
}

If I didn't pass the ISearcher into ItemLocator then it would have to find an ISearcher to use on it's own. That would prevent me from providing the particular ISearcher I want the class to use during testing and prevent me from passing in a mocked implementation which I can easily orchestrate. In Sitecore, I often find I still require a parameterless constructor. For example, for pipeline processors, so the pipeline can be created by the pipeline factory. In these cases you can provide a parameterless constructor which uses default dependencies. C#

public class ItemLocator
{
  private readonly ISearcher searcher = null;

  public ItemLocator() : this(new BasicSearcher())
  {
  }

  public ItemLocator(ISearcher searcher)
  {
    this.searcher = searcher;
  }
}

If you were going for a proper IoC approach you'd of course read those dependent types from your IoC container, from settings or use the Sitecore Factory.

Dependencies in Disguise

Some dependencies in your code are not as explicit as the example above. Static types and methods are a type of dependency but are harder to spot. By using a static you're preventing that type from being passed into the type being constructed for testability. There are of course limits to this advice. int.TryParse() is a static method but probably not one you would consider a dependency and probably not one you'd care to orchestrate for testing. The scope of that method is very small and encapsulates a well defined task which never needs to change depending on the context. Just a quick side step on that int.TryParse() example above. You may need to ask yourself if your abstraction is correct. I've seen plenty of code where general types (strings) were being provided from a web request and then passed into a class pushing the responsibility of parsing the correct data from the string to the class. But is that the classes responsibility? No, it's not. The responsibility for interfacing with the outside world should sit with the web request handler. The handler should parse the data from the string and pass the strongly-typed data to the class. This is an example where proper abstraction can extract a dependency. For testability, we want to avoid using static and instead pass the dependency in through DI. Another dependency that can be hard to spot is global data. Using global data means your test needs to set that global data before exercising your type. In some cases setting this data may be trivial, but in others the global data may be essentially read only. A classic example of this in Sitecore terms would be the use of Sitecore.Context.Item. By using the context item we can't use that same code to operate on an item which is not the context item. So from the test code we have to fiddle with setting the context item before exercising the code under test. This is not as explicit as passing the item to operate on into the type being tested. For testability, we want to avoid accessing global data and instead only operate on data passed into the type. This can be hard in practice, especially if the data you need to process is global. And let's not get stuck talking about how global data is inherently evil and should be avoided. For testability we need to pass the data in, but you can always have an overload that calls the other method, passing the global data. Here's an example of a method which normally operates on global data (the Sitecore context Item), but provides an overload for testing. C#

public class TitleGenerator
{
  public string GetTitle()
  {
    return GetTitle(Sitecore.Context.Item);
  }

  public string GetTitle(Item item)
  {
    // logic to extract title
    return title;
  }
}

Don't ignore Demeter

Observe the Law of Demeter, or in other words, provide only what is needed to your classes. We all know how hard it can be to mock a Sitecore Item. Or if we're not mocking the item we're creating real items in a real database and OMG we just fell off the cliff and became an integration test. Do you really need the entire item in your method or are you just using it's ID, or template, or some other piece of data which could be passed in instead? Consider these two methods: C#

public class LoD
{
  public bool TemplateDescendsFrom(Item item, TemplateItem template)
  {
    // logic here that only uses item.TemplateID
  }

  public bool TemplateDescendsFrom(ID itemTemplateId, TemplateItem templateId)
  {
    // logic here
  }
}

Which of these do you think will be more easily testable? For the first method we'd need a real item to pass into the method inside our test. But in the second, we just need the ID of the template the item is based on. We can easily create an ID in our test code.

Conclusion

I've found that following these tips has helped me when writing tests for my classes. I hope they helop you too.

Comments

mawkstiles

Speaking of abstract, don't you just love where 'digital' is headed these days: http://www.fine-digital-art.com/abstract-art-gallery/attachment/5977/

mawkstiles

Jokes aside, this is one of those topics that's so abstract that diagrams might be the best way to explain it.
Tightly coupled classes are like chains and it's easier to test many small chains than a few really large chains. It might also be quicker to recognize problem areas where chains become like webs.
Hmmmm. (note to self...)

Leave a comment

All fields are required.