Geeks With Blogs
Boy Meets 'Hello World' Blogging the journey from College Grad to .NET Developer

One of the common scenarios I find myself in is needing to be be able to mock an object instantiated during the lifecycle of another object. For example, a service object might need to, on receiving one message, create an entity, and on receiving another message, call some method on that entity. The easy way to do this is to make a builder as a service for the object needing construction, and mock out the builder object to return a mock. So, for that service-layer code I'm trying to test...

   1:  public class MyService
   2:  {
   3:      private readonly MyBuilder builder;
   4:      private readonly MyEntity entity;
   6:      public MyService(MyBuilder builder)
   7:      {
   8:          this.builder = builder;
   9:      }
  11:      public void OnSomeMessage(Message1 message)
  12:      {
  13:          entity = builder.Build();
  14:      }
  16:      public void OnSomeOtherMessage(Message2 message)
  17:      {
  18:          entity.DoSomething();
  19:      }
  20:  }


Of course, this class is highly stripped down. There should be some error handling if, for example, Message2 is passed first (entity will be null in that case).

I can mock out my entity in the OnSomeOtherMessage method by passing in a mock MyBuilder object to return my mocked entity. When I need to eventually create the implementation for the MyBuilder object, it will look something like this...

   1:  public class MyBuilder
   2:  {
   3:      public MyEntity Build()
   4:      {
   5:          return new MyEntity();
   6:      }
   7:  }


This is fine, although you really can't test MyBuilder. However, even though I see myself as a TDD enthusiast, I don't think I need to test this MyBuilder class. Even if the constructor had args, I feel fine just leaving this as it is without tests. But now, assume that my entity is getting a bit more complex, and that there is a new rule that whenever this object is created, a message is sent out. This message should contain information that the entity holds, but the logic is too complex to do in the constructor. I've decided that an Initialize() method must be put on the MyEntity object. So, where do I call that Initialize method?

My gut reaction would be, in the builder. However, now because my builder has a bit of logic in it, I'm going to want to be able to test that builder to make sure that after it creates an entity, it calls this Initialize() method on it before returning. But, I run into that same problem of how do I mock the MyEntity object. Last time I ran into this problem, I added a builder, so let's do that...

   1:  public class MyBuilder
   2:  {
   3:      private readonly NoReallyThisIsTheBuilder builder;
   5:      public MyBuilder(NoReallyThisIsTheBuilder builder)
   6:      {
   7:          this.builder = builder;
   8:      }
  10:      public MyEntity Build()
  11:      {
  12:          var entity = builder.Build();
  13:          entity.Initialize();
  15:          return entity;
  16:      }
  17:  }


Ok, this is just getting silly. I'm not going to really create another builder object. I felt dirty enough creating the first builder,  I'd hate to just keep making turtles all the way down.

This situation is easily solved with TypeMock

   1:  [Test]
   2:  public void TestObjectInstantiation()
   3:  {
   4:      MockManager.Init();
   6:      var theMock = MockManager.Mock(typeof(Entity), Constructor.StaticNotMocked);
   8:      theMock.ExpectConstructor(1);
   9:      theMock.ExpectCall("Initialize", 1);
  11:      var builder = new Builder();
  12:      var results = builder.Build();
  14:      Assert.That(results, Is.EqualTo(theMock.MockedInstance));
  16:      MockManager.Verify();
  17:  }


But I'm not going to use a whole new framework just to support one style of test. If I used TypeMock more often, I'd probably go with this, but since I don't, I won't. Instead, I'll go with making a test-specific subclass...

   1:  public class Builder
   2:  {
   3:      protected virtual Entity BuildNewEntity()
   4:      {
   5:          return new Entity();
   6:      }
   8:      public Entity Build()
   9:      {
  10:          var entity = BuildNewEntity();
  11:          entity.Initialize();
  13:          return entity;
  14:      }
  15:  }
  17:  public class TestSpecificBuilder : Builder
  18:  {
  19:      private Entity entity;
  21:      public void SetStartingEntity(Entity startingEntity)
  22:      {
  23:          if (startingEntity == null) throw new ArgumentNullException("startingEntity");
  25:          entity = startingEntity;
  26:      }
  28:      protected override Entity BuildNewEntity()
  29:      {
  30:          return entity;
  31:      }
  32:  }


Slightly off-topic, but here's my take on TypeMock, which has been the center of controversy in the ALT.NET spectrum. Here's one situation where I find it helpful. The problem is, there isn't many other situations where I say that, and using a test-specific subclass is fine by me.That isn't to say there isn't any situations where you might use it. If you are doing a project with more lower-level code that is very close to the .NET framework in all it's sealed goodness, or perhaps working with legacy code, it would probably be more useful, but so far in my travels I haven't found many situations where it was miles beyond just using proper object relations.

In fact, the same probably goes for most mocking frameworks. I use them to mock objects, not create test seams. Most of my objects have only one or two methods on them, so writing a hand-written mock is pretty simple, and makes the tests easier to read ("EntityWasInitializedOnce" vs. "Expect.Call(entity.Initialized()).Times(1)"). I find my test seams mostly create themselves (with the above being one of the few examples where they don't). But remember, my experience with TypeMock goes as far as me downloading it this morning and creating that test I wrote above, so use your own judgement.

Posted on Saturday, June 7, 2008 8:00 AM Unit Tests , TheGame , Design | Back to top

Comments on this post: On Testing Builder Objects

No comments posted yet.
Your comment:
 (will show your gravatar)

Copyright © mhildreth | Powered by: