Tuesday 7 September 2010

Don't Mock Me, I'm a Classist

I have to admit to being an immediate convert with NUnit as soon as I was introduced to it around 5-6 years ago. So much so that not only could I not conceive ever developing without it, but (rather arrogantly) I couldn't understand why everyone wasn't fully test-infected, just like me.  Forget the fact that it had been around the Java world for some time already and I just hadn't the presence of mind to take note - no, no, no. This was it. This was my Ground Zero, and why wasn't everyone else in on it? Heathens!
I got off my high horse sometime later and thankfully, over the past few years I have noticed that in the environs that I work, that automated unit and/or integration testing has slowly but surely become more popular.  All teams that I work with just know that it is a given; automated testing is not up for discussion, its just there.  Its like a joiner turning up for work without a hammer - it doesn't happen. Its such an inherent part of implementation that when I ask developers for implementation estimates, these estimates should (and will!) include time for unit testing.  I don't think its fully emersed to that extent everywhere just yet, but at least it will be in the good dev shops.

With such growth in popularity comes not only new ideas, but new opinions on how things should be done. When it comes to testing frameworks, the new ideas and opinions are usually based around using a mocking framework (such as RhinoMocks, NMock or the more recent Moq) rather than just simply NUnit.
I have tried mocking frameworks on multiple projects, and personally to this day I am yet to be fully convinced.  There are 2 separate elements of functionality offered by most mocking frameworks:
  1. Using a mocking framework to generate stubs.
  2. Using a mocking framework for mocking.
Let me make this clear, using a mocking framework for stub class generation, I love.  I'm sold.  It saves me time and effort, and makes my tests simpler and more lightweight, but using a mocking framework to generate stubs is not mock testing.  Fowler's paper Mocks Aren't Stubs explains why so much better than I could ever hope to.  However, providing the additional stubbing functionality has ironically muddied the waters for a lot of people on what mocking actually is.  And that is a shame because I'm still looking for someone I know to successfully mock their production code, providing adequate code coverage and flexible test harnesses, such that I would be convinced to give it another go.

Onto the mocking itself.  The flexible tests harnesses in particular is where I have trouble with mock testing.
Classic unit testing is easy to understand and adopt, with the test-infected mantra of Red-Green-Refactor.  The way I see it, the refactoring step becomes more difficult when you have tests mocking the internal behaviour of your classes; to me your tests are inherently brittle when you are double-checking the inner workings of your code.  Now, guys like Ayende have forgotten more than I will ever know, so I don't doubt their concepts but to this day, every time I sit down and try to test my code by mocking, I can only see brittle tests in front of me - and that is why, as Fowler says, I'm a classist rather than a mockist.

On a lighter note, on first read of Fowler's Mocks Aren't Stubs many moons ago, I thought that "traditionalist" would have been a better term rather than "classic" or "classist" to describe the original automated testing practice.  Second thoughts led me to think otherwise.  Modern classics will always be there, and will always be used, because...well, they're classics aren't they? I will always listen to The Stone Roses first album, and Teenage Fanclub's Bandwagonesque.  Not because they are traditional, but because they are just good (no matter how much some kid in skinny jeans tries to tell me otherwise). Its the same with classic unit testing.  Sure, the tools may become better, and fancier but the basic concept is sound and will always be practiced.
I'm not sure whether Fowler gave too much thought about his terminology, but in this profession, where it is difficult to convey conceptual ideas well, naming something correctly is vital. Whether he meant it or not, he is on the money (and not for the first time either).