Friday 29 June 2012

Why do I consider interfaces superior to classes for mocking?

There's a debate of using classes instead of interfaces for mocks. You get a class mock by taking a class that has at least some of its public methods virtual and make a subclass where you override those methods and inject your own behaviors. Proponents of this technique point out the following advantages over interface mocks:

  1. No interface bloat
  2. Easier navigation in IDEs (pointing to method usage and choosing an option like "go to definition" takes you to the method implementation rather than the interface signature)
  3. No need to maintain another set of method signatures in interfaces - less work when changing things.

Personally, I myself use class mocks very rarely. For the following reasons:

1. Trouble overriding the implementation.

When you intend to mock a class, you have to watch out how you implement it. There are parts of class implementation that cannot be overridden, and these parts, when done wrong, may affect your specs/Unit tests in an unexpected way. Let's examine the following, bad from mocking perspective, example of a class:

public class PersonDao
{
  public PersonDao()
  {
    if(!wrappedTable.Exists)
    {
      wrappedTable.Create();
    }
  }

  public virtual void Close()
  {
    connection.Close();
  }

  private Table wrappedTable 
   = new Table("Person", "CreatePersonTableDdl.sql");
  private Connection connection = Connection.Open();
}

This is an example of a simple DAO class.

Let's take a look at the constructor first - someone tried to be clever and wanted to make sure that whenever we create a data access object, the table wrapped by it exists. This is bad, because creating a mock of this class (which is almost always about creating a subclass at runtime - this is how most mocking libraries work) ends up invoking the class's constructor. The effect? Each time we make a new mock, an attempt will be made to access the database and there's nothing we can do about it (apart from setting up a database on every machine that runs your unit tests, but believe me, you don't wanna do it).

Now look at the bottom of this class definition - here we've got an inline initialization of two fields, one of which is initialized with an open connection, which also cannot be mocked - it will always be executed when creating instances of this class or any subclass (including a mock). The only way to deal with such static method invocation as seen here is to add some extra implementation to the Connection class that would allow us to somehow set a fake instance to be returned by this method. But let's imagine that this is actually a third party class and we have no access to the source code. What can we do? again - nothing.

There's only one way to deal effectively with issues described above - we have to change the design to allow dependency injection, plus, such design has to be maintained as the code evolves. My practical experience says it's unrealistic to expect every programmer that works with you to understand design guidelines that apply to mockable classes. Even if you train every developer in your team, sooner or later someone new will come and make changes against those guidelines. So, either turn into a policeman or give up.

2. Maintaining virtuality

In order to be able to mock a method inside a class, the method has to be virtual. It's best to keep all methods virtual in order to keep the mocks maintainable - when some of the methods are virtual and some of them not, it's hard to actually figure out what actually gets called in given circumstances and hard to read specifications that use such mocks - each time you read a spec, you have to go to the class definition to check whether the method in question will be mocked or not. Again, all of this requires everyone to follow strict class design guidelines, which is troublesome to enforce.

3. Constructor maintenance

Given such class:

public class MockedObject
{
  public MockedObject(int arg)
  {
  }
}

..Let's execute this code using a popular Moq mocking framework:

[Test]
public void ShouldAllowSubstitutingConcreteObjects_Moq()
{
  var x = new Mock<MockedObject>();
  var y = x.Object;
}

..or this one using NSubstitute framework:

[Test]
public void ShouldAllowSubstitutingConcreteObjects_NSubstitute()
{
  var x = Substitute.For<MockedObject>();
}

Both Moq and NSubstitute rely on Castle.Proxy library to create proxy objects (i.e. runtime objects subclassing the class). So no wonder both of these snippets give you the same result:

System.ArgumentException: Can not instantiate proxy of class: 
Playground.OutExample+MockedObject.
Could not find a parameterless constructor.
Parameter name: constructorArguments
  at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance...

Why is that? In both cases we create a new mock without supplying any parameters to the constructor, which takes one parameter - even when creating a subclass, we still have to go through superclass's constructor (because it's the only constructor available). Now let me show you a corrected version of both of these snippets:

[Test]
public void ShouldAllowSubstitutingConcreteObjects_Moq()
{
  var x = new Mock<MockedObject>(Any.Integer());
  var y = x.Object;
}

[Test]
public void ShouldAllowSubstitutingConcreteObjects_NSubstitute()
{
  var x = Substitute.For<MockedObject>(Any.Integer());
}

It means every time the constructor changes, we have to update the constructor's parameter list in each mock creation, which makes such mocks hard to maintain.

All of these points make me accept gladly the interface bloat and mock interfaces rather than classes. I tend to use class mocks in some special cases only (like specifying abstract classes by mocking abstract methods and specifying how non-abstract methods of the same object use them).

And that's it. Maybe next time I'll show you how to deal with some of the downfalls of class mocks mentioned here. In the meantime, take care and enjoy your weekend ;-)

No comments: