Kash Farooq's software development blog

.NET Developer

TDD: Using a Func instead of a Factory

Posted by Kash Farooq on August 10, 2009

In this post I’ll look at using a Func to control object creation, which allows you to reduce the amount of code you need to write to make something testable.

Consider the following code:

public class SystemUnderTest {
  //A method that is currently impossible to test
  public void MyMethod() {
    using (var otherClass = new SomeOtherClass()) {
       if (!otherClass.DoWork()) {
          throw new ApplicationException("DoWork failed");
       }
     }
  }
}

SomeOtherClass looks like this:

public interface ISomeOtherClass : IDisposable {
  bool DoWork();
}

public class SomeOtherClass : ISomeOtherClass {
  public void Dispose() {}
  public bool DoWork() {
    return true;
  }
}

In it’s current state, SystemUnderTest cannot be tested. The SomeOtherClass object creation is happening inside the public method, so cannot be controlled from a unit test. We cannot test that if otherClass.DoWork returns false, an exception is thrown. We cannot test that otherClass.Dispose is called.

To make it possible to control the object creation from a test, we can introduce a factory class and interface and inject this into the SystemUnderTest:

public class SystemUnderTest {
  private readonly IFactory factory;
  public SystemUnderTest(IFactory factory) {
    this.factory = factory;
  }
	
//Default constructor creates the concrete Factory
  public SystemUnderTest():this(new Factory()) {}
	
  public void MyMethod() {
    using (var otherClass = factory.Create()) {
       if (!otherClass.DoWork()) {
          throw new ApplicationException("DoWork failed");
       }
    }
  }
}

//The interface we inject into SystemUnderTest to control the object creation:
public interface IFactory {
   ISomeOtherClass Create();
}

//The concrete implementation of IFactory just returns a new SomeOtherClass object:
public class Factory : IFactory {
  public ISomeOtherClass Create() {
    return new SomeOtherClass();
  }
}

Now, we can test this code:

[Test]
public void MyMethodThrowsExceptionIfDoWorkReturnsFalse() {
  //Mock a factory and set it up to return a mock SomeOtherClass object:
  var factory = MockRepository.GenerateStub();
  var someOtherClass = MockRepository.GenerateStub();
  factory.Expect(x => x.Create()).Return(someOtherClass);
  someOtherClass.Expect(x => x.DoWork()).Return(false);

  var systemUnderTest=new SystemUnderTest(factory);
	
  //Ensure that as DoWork returned false, an exception is thrown:
  try {
    systemUnderTest.MyMethod();
    Assert.Fail("Expected exception not thrown");
  }
  catch (ApplicationException) { }
	
  //Ensure dispose is always called:
  someOtherClass.AssertWasCalled(x => x.Dispose());
}

So, to write this test and to get SystemUnderTest working, we have had to introduce IFactory and a concrete implementation of Factory. That’s a lot of code to do something pretty simple.

We can do away with these two new code files if we use a Func to create the object:

public class SystemUnderTest {
  //A Func acting as a factory 
  private readonly Func<ISomeOtherClass> factory;

  public SystemUnderTest(Func<ISomeOtherClass> factory) {
    this.factory = factory;
  }
	
  //Default constructor creates the concrete SomeOtherClass object with a Func
  public SystemUnderTest():this(() => new SomeOtherClass()) {}
	
  public void MyMethod() {
    using (var someOtherClass=factory()) {
      if (!someOtherClass.DoWork()) {
          throw new ApplicationException("DoWork failed");
      }
    }
  }
}

I’ve still called the Func “Factory” to describe what it is doing – it is just creating an object.
The test now injects a Func to control the object creation:

[Test]
public void MyMethodThrowsExceptionIfDoWorkReturnsFalse() {
  var someOtherClass = MockRepository.GenerateStub();
  someOtherClass.Expect(x => x.DoWork()).Return(false);

  //Inject a func that returns my mock SomeOtherClass object:
  var systemUnderTest = new SystemUnderTest(() => someOtherClass); 
  try {
   systemUnderTest.MyMethod();
   Assert.Fail("Expected exception not thrown");
  }
  catch (ApplicationException) {}

  someOtherClass.AssertWasCalled(x => x.Dispose());
}

We have eliminated the need for the interface IFactory and the class Factory, and reduced the amount of mocking we had to do.

Advertisements

2 Responses to “TDD: Using a Func instead of a Factory”

  1. Rob said

    Nice use of the Func, but why bother having a factory at all when the client could be responsible for instantiating the instance of SomeOtherClass? I would only use a factory if it was necessary i.e. there was some complicated/conditional behaviour required to determine how something is instantiated.

    • kashfarooq said

      Granted, it’s not normal to use a factory that does nothing but always create a single type of object. However, my factory in this example is only required to make this piece of code testable.

      If you passed an instantiated SomeOtherClass into the SystemUnderTest, you couldn’t dispose it with the using statement. (For example, say SomeOtherClass was some sort of IO Stream that had to be closed).

      So, okay, you could push that creation and disposing functionality into the client, then how would you TDD the client…

Sorry, the comment form is closed at this time.

 
%d bloggers like this: