Testa-what?

I don't know if "testability" is a word, but if Bud Light can make up "drinkability", maybe it should be.  I'm talking about how pliant your code is to testing.  If you're doing TDD or BDD, the code you write is testable by definition.

But if you're going the other way, and coding first or testing legacy code (any code not already under test), you've probably run into code that was hard to test and felt like giving up.  Some of the most common hurdles for testability are not coding to abstractions and not decoupling dependencies.  Here's how to get around that.

The problem

Here's a garden variety Order class with a ProcessOrder method:

public class Order
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Email { get; set; }
  public decimal Total { get; set; }

  public bool ProcessOrder()
  {
    var result = false;

    // Validate order
    if (!string.IsNullOrEmpty(this.Email) && this.Total > 0)
    {
      // Send order confirmation email
      var emailService = new EmailService();
      var emailSendResult = emailService.SendOrderConfirmationEmail(this);

      if (emailSendResult == true)
      {
        // Do more order processing...

        result = true;
      }
    }

    return result;
  }
}

And here's the test:

[Test]
public void Should_return_true_when_using_good_order_values()
{
  // Arrange
  var order = new Order();
  order.FirstName = "Joe";
  order.LastName = "Wilson";
  order.Email = "real_email_address@volaresystems.com";
  order.Total = 123.00m;

  // Act
  // Watch out!  Don't run this or it will send real emails!
  var result = order.ProcessOrder();

  // Assert
  Assert.That(result, Is.True);
}

We need to call the SendOrderConfirmationEmail method in the EmailService class.  But if our test calls ProcessOrder, there is no way to avoid calling the live emailing method as it's coded now.

How can we test the logic in the ProcessOrder method without sending out a real email?

Code to Abstractions

First, we need to code to an abstraction of EmailService, not to the concrete class itself.  So let's create an interface around EmailService:

public interface IEmailService
{
    bool SendOrderConfirmationEmail(Order order);
}

Then change the EmailService class to implement this new interface.  Visual Studio 2008 and ReSharper make this easy.

This will let us change our test later to use a fake version of IEmailService instead of the real one.

Decouple and Invert your Dependencies

The next problem is we're creating an instance of the EmailService class in the ProcessOrder method.  Anytime you use "new" in your code, you're probably introducing a dependency and reducing testability.

The fix is to let the caller create the instance and tell the class or method being called which instance to use.  I am using a private field with constructor injection to set the field instance.  Note we're also now coding to the interface IEmailService instead of the instance EmailService.

Here's the full Order method using the private field to call the SendOrderConfirmationEmail method on IEmailService inside the ProcessOrder method.

public class Order
{
  private readonly IEmailService _emailService;

  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Email { get; set; }
  public decimal Total { get; set; }

  public Order(IEmailService emailService)
  {
      _emailService = emailService;
  }

  public bool ProcessOrder()
  {
    var result = false;

    // Validate order
    if (!string.IsNullOrEmpty(this.Email) && this.Total > 0)
    {
      // Send order confirmation email
      //var emailService = new EmailService(); // Created outside this class now
      var emailSendResult = _emailService.SendOrderConfirmationEmail(this);

      if (emailSendResult == true)
      {
        // Do more order processing...

        result = true;
      }
    }

    return result;
  }
}

Dependencies can be very hard to spot, but it's critical to tease them out of your code to increase testability.  They can even come from things as innocuous as DateTime.  If you're code relies on DateTime.Now, how will you write unit tests for your logic without changing your system clock when you run the test?  It's simpler to have the caller pass in the DateTime value and have the method do the calculations based on whatever value it is given.

Make a Fake

Now that we've got testable code, we can make a fake EmailService and update our test.

First, create the FakeEmailService class implementing the IEmailService we created earlier.  We need the SendOrderConfirmationEmail method to return true to simulate an email being successfully sent.

public class FakeEmailService : IEmailService
{
  public bool SendOrderConfirmationEmail(Order order)
  {
    return true;
  }
}

You could also use a mocking framework like Moq or Rhino Mocks for this, but a homemade fake is easy, too.

Now we're ready to update our test to use the fake and inject it into the Order constructor:

[Test]
public void Should_return_true_when_using_good_order_values()
{
    // Arrange
    var emailService = new FakeEmailService();
    order = new Order(emailService);
    order.FirstName = "Joe";
    order.LastName = "Wilson";
    order.Email = "real_email_address@volaresoftware.com";
    order.Total = 123.00m;

    // Act
    var result = order.ProcessOrder();

    // Assert
    Assert.That(result, Is.True);
}

Last step

We've refactored the Order class so the caller sends the IEmailService instance to it.  That makes sense for the test, but what about the real code?

I typically use an IOC container like Structure Map or Castle Windsor to hold and resolve an in-memory dictionary of interfaces and class instances.  You wire it up at application startup and when you call something that needs IEmailService, it creates an instance of EmailService for you.

If you're working with legacy code and can't use and IOC container, you also can add a no arg constructor like this:

public Order() : this(new EmailService())
{
}

public Order(IEmailService emailService)
{
    _emailService = emailService;
}

Your legacy code keeps calling the no arg constructor like it used to, and any new code and your test code use the constructor with IEmailService dependency passed in.

Wrap up

If you code isn't testable, you won't test it.  If you're working with legacy code or writing tests after coding, you can use these techniques to pull out external dependencies so they can be faked for testing.

You can have it all: testable code, unit tests around that code, and no broken legacy code. Mmmm.Testability. Drink it in!