The Dangers Of AutoMocking Containers


Louis Salin commented on my original post about the Ninject.RhinoMocks automocking container, and brought up a very good point. Here is his comment, reproduced in full:

I’ve heard (or read…) that automocking is equivalent to taking weight loss pills while still eating cheesburgers for breakfast. Okay, I just made that up!

My point is, and I’m in no way in a position to opine on the matter, that the pain of mocking might be due to design issues. Hiding the pain with a tool won’t make the cause go away.

So maybe in this case it’s a very benign use of an automocker, but as the code base grows, the automocker will hide pain points that would otherwise become immediately obvious, no?

Louis has a good point and it is one that I have argued in the past to justify why I have not used an auto mocking container. However, I stand by my response in the comments of that post:

yeah, that’s the “big problem” that people complain about when they say auto mocking containers are bad. honestly, that’s a pretty weak excuse for not teaching developers how to spot too many dependencies as a part of bad design. trust your team. if they get it wrong, teach them right.

The “pain of mocking” that Louis is referring to is most often the need to mock a significant number of things in order to get a class spun up for testing. It may be painful or tedious or however you want to describe it, to get all of the things you need setup in order to get a class under test. But just because you can ignore that pain with an automocking container, doesn’t mean you will.

Before I expand on my response, though, let’s look at an example of what the problem really is.

 

A Simple Specification

This is the same sample specification that I ended yesterday’s blog post with. It’s small, easy to read and easy to understand. There is nothing really wrong with this code, in my opinion.

   1: public class when_doing_something_with_that_thing : ContextSpecification<MyPresenter>

   2: {

   3:     protected override void When()

   4:     {

   5:         SUT.DoSomething();

   6:     }

   7:     

   8:     [Test]

   9:     public void it_should_do_that_thing()

  10:     {

  11:         AssertWasCalled<IMyView>(v => v.ThatThing());

  12:     }

  13:  

  14:     [Test]

  15:     public void it_should_do_the_other_thing_twice()

  16:     {

  17:         AssertWasCalled<IMyView>(v => v.TheOtherThing(), mo => mo.Repeat.Twice());

  18:     }

  19: }

</div> </div>

The problem that an automocking container hides is not this code, but what this code potentially hides in the implementation.

 

A Complex Implementation

Now take a look at one possibility for the implementation of the MyPresenter class used in the above specification:

   1: public class MyPresenter

   2: {

   3:     private IMyView view;

   4:     private ISomeService someService;

   5:     private IAnotherService anotherService;

   6:     private IMoreService moreService;

   7:     private ISomeRepository someRepository;

   8:     private IAnotherRepository anotherRepository;

   9:     private IMoreRepository moreRepository;

  10:     private IValidator<SomeData> someDataValidator;

  11:     private IValidator<MoreData> moreDataValidator;

  12:     private IValidator<AnotherData> anotherDataValidator;

  13:     private SomeData someData;

  14:     private AnotherData anotherData;

  15:     private MoreData moreData;

  16:     

  17:     public MyPresenter(

  18:         IMyView view,

  19:         ISomeService someService,

  20:         IAnotherService anotherService,

  21:         IMoreService moreService,

  22:         ISomeRepository someRepository,

  23:         IAnotherRepository anotherRepository,

  24:         IMoreRepository moreRepository,

  25:         IValidator<SomeData> someDataValidator,

  26:         IValidator<MoreData> moreDataValidator,

  27:         IValidator<AnotherData> anotherDataValidator

  28:     )

  29:     {

  30:         this.view = view;

  31:         this.someService = someService;

  32:         this.anotherService = anotherService;

  33:         this.moreService = moreService;

  34:         this.someRepository = someRepository;

  35:         this.anotherRepository = anotherRepository;

  36:         this.moreRepository = moreRepository;

  37:         this.someDataValidator = someDataValidator;

  38:         this.moreDataValidator = moreDataValidator;

  39:         this.anotherDataValidator = anotherDataValidator;

  40:     }

  41:     

  42:     // ... methods and implementation details go here

  43: }

</div> </div>

That’s 40 lines of code just to get the object constructed! ARGH! that’s AWFUL! There are so many things wrong with just the member variables listed in the constructor of this class… and I haven’t even begun to imagine what the implementation of any methods on this class may look like. Quite honestly, I don’t want to think about what they may look like.

Now imagine that this code uses a couple of abstract base classes instead of all interfaces for the dependencies. Replace the three validators, for example, with abstract base classes. What happens when each of these base classes requires 2 constructor arguments for their own dependencies? The auto mocking container will go ahead and wire them up as well, and pass them into the abstract base classes so that the objects can be mocked. Now, instead of having 10 objects being mocked, we have 16. If any of those dependencies are objects with their own dependencies… well, I think you get the picture. The object graph being automocked in this scenario is horrendous and wreaks of bad design left and right.

But because we have an automocking container, we don’t care about that bad design, right? We’ll just let it slip and go on about our business because the pain of that horrendous mess is hidden away at test time. Our tooling of choice makes it easy to get away with poor design… or so the argument goes.

 

The Truth About Auto Mocking Containers

There is nothing inherently evil in auto mocking containers. They are not “bad” and using them is not “wrong”. Sure, they can be abused and you can do damage with them. The same thing is true of baseball bats, eggs, automatic rifles, and thousands upon thousands of other tools. Scott Bellware has quoted Ani DeFranco on the subject of tools, more than a few times: “every tool is a weapon if you hold it right”. 

Now… let me restate my opinion on the problem that Louis is referring to, keeping in mind that I used to cite this exact reason for my not wanting to use an auto mocking container.

Auto mocking containers do not facilitate poor design or horrendous implementation. Poor design and horrendous implementation skill, in the person designing and implementing the code, does.

That’s it, right there. The notion that an automocking container will let someone design and implement that pile of garbage, while not using one won’t let them or will expose the problem, is ridiculous.

Speaking as a person who used to write garbage like this (and still does, occasionally, I’ll admit), I know that not using an automocking container will not prevent you from doing this. It will not make the problems more obvious if you don’t use an automocking container, and you will not inherently write better code without one. A software developer who writes code like this is not going to know the problems they are causing just because they have to declare and instantiate the 10 mock objects that this code needs to be tested. Developers write code like this because that’s the kind of code they right… no other reason. Now, there might be a lot of reasons why they write code like this… but that’s a completely different set of subjects.

“a poor craftsman blames his tools” … “with great power comes great responsibility” … “(insert other overused and abused quotes here)”

 

One Last Note

I wanted to note, specifically, that this post is not directed at Louis or anyone in particular. Louis is only the guy that prompted the discussion and not a person that I would single out for writing bad code.

Simplify Your Unit Tests With Automocking: Part 2 – Establishing Context