Coding and Dismantling Stuff

Don't thank me, it's what I do.

About the author

Russell is a .Net developer based in Lancashire in the UK.  His day job is as a C# developer for the UK's largest online white-goods retailer, DRL Limited.

His weekend job entails alternately demolishing and constructing various bits of his home, much to the distress of his fiance Kelly, 3-year-old daughter Amelie, and menagerie of pets.

TextBox

  1. Fix dodgy keywords Google is scraping from my blog
  2. Complete migration of NHaml from Google Code to GitHub
  3. ReTelnet Mock Telnet Server à la Jetty
  4. Learn to use Git
  5. Complete beta release FHEMDotNet
  6. Publish FHEMDotNet on Google Code
  7. Learn NancyFX library
  8. Pull RussPAll/NHaml into NHaml/NHaml
  9. Open Source Blackberry Twitter app
  10. Other stuff

Unit Testing Good Patterns #2 - Fluent Builder Pattern in C#

Hi all,

Today I'd like to share my experience with the builder pattern, and how it can radically improve the brevity and readability of your tests. Don't you just hate unit tests that have a whacking big "Arrange" section, one that fills up your whole monitor screen? While it's not a magic bullet, the Builder pattern can help with this.

Simply put, the Builder pattern involves creating static classes with static methods designed to new-up instances of your business-entity classes, populated with common and typical data. Used with some extension method goodness, we'll also see how easy it is to create builder classes using nice fluent interfaces.

Arrange - The Wrong Way

When I first started dabbling with unit tests, I'll admit one of the last things on my mind was code reuse. I was pretty fixed on these unit tests being self-contained "units", so the idea of adding in more external references to things like shared code didn't sit right. Of course this view didn't last long, certainly when tested against legacy code, it gets really painful really quickly.

Typically, my arrange code was starting to look like this:

IList<Permission> permissions = new List<Permission>
    {
      new Permission(PermissionType.Read),
      new Permission(PermissionType.Write),
      new Permission(PermissionType.Delete)
    };
User user = new User(1, "UserName") {
  Role = UserRole.Administrator,
  Permissions = permissions,
  Site = new Site("Preston")
};

Arrange - The Right Way

In reality, that's all totally standard configuration for a user, the only part that's different for my particular test is that this particular user has "Delete" permissions.  So why not re-write the code as follows?

User user = UserBuilder.Create(1, "UserName")
    .WithDeletePermissions();

We're now asking for a builder to give us a typical user object pre-populated, and we can then customise it to only add or remove the non-standard things that our test requires.

My Example Builder Class

There's actually very little genious ninja-esque code here, so I'm just going to rattle out the builder class that I would need to allow the above arrange notation.

public static class UserBuilder
{
  public static User Create(int userId, string userName)
  {
    IList<Permission> permissions = new List<Permission>
{ new Permission(PermissionType.Read), new Permission(PermissionType.Write) }; User user = new User(1, "UserName") { Role = UserRole.Administrator, Permissions = permissions, Site = new Site("Preston") }; return user; } public static User WithDeletePermissions(this User user) { // Some basic code here to validate the user object passed in user.Permissions.Add(new Permission(PermissionType.Delete)); return user; } }

About the only interesting point in the above code is the extension method that allows you "decorate" the object returned from the builder, this relies on features added in .Net 3.5, and it's pretty awesome stuff.  (IMHO of course)

Troubleshooting - I Can't Call my Extension Method

Update 26th May - I've just had a chat with some co-dudes here at work who have hit an interesting problem! If you're calling your builder by "Builders.UserBuilder.Create()", you may find you can't get your extension methods! You need to have the UserBuilder class available to you without qualifying the namespace, otherwise your extension methods are invisible. So you might need to have "using ....Builders" at the top of your test class, and you can then just call "UserBuilder.Create().WithDeletePermissions()".

Where to Place your Builder Classes

These types of builder classes assume a lot of things when creating objects, that I don't feel are healthy in production code.  These assumptions are all about short-cuts because you're unit testing, whereas your application code runs in the context of a complete application, with a valid data repository, or valid services returning (hopefully) well formed results.

My conclusion from this?  Your builder classes should not live in your production assemblies, they should instead live in your test assemblies.  More specifically, if you have a test assembly per production assembly, your builders should live in your "BusinessEntities.Tests" assembly, and each of your test assemblies that required your builders should then reference them from your "BusinessEntities.Tests" assembly.

That's it for now, take care all,

Russ


Permalink | Comments (0)

Pingbacks and trackbacks (1)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading