August 2, 2015

...Learn TDD with Codemanship

Mocking Without Mocks - The Birth of JMicroMock

I was playing around with a code example to illustrate for trainees the difference between mocks and stubs. I've found a good way to get he message across is to show them how hand-rolled interaction test doubles are strictly speaking "mocks", even though we didn't use a mocking framework to create them. (And, conversely, objects that return test data are "stubs" even when we use a mocking framework to create them.)

In experimenting with mocking without mocks, I initially came up with this:

What struck me is that this code looks not much more complex than the equivalent code might using a mocking framework like Mockito or EasyMock. Even so, I couldn't resist taking it a bit further, thinking about how consistent and helpful failure messages might be generated by a dedicated MockExpectation class instead of the rather more brittle technique of boolean flags and hard-coded strings:

Then it struck me that MockExpectation was possibly doing too many things - more than one reason to change - so I refactored it into 3 classes that checked the expectation, compared arrays of expected and actual parameter values, and built the failure message using the available information. It also occured to me that the hard-coded method name was also possibly a little brittle - how soon might it get out of step with the code? So I added an extra check to make sure the method invoked matched the name of the method we expected to be invoked, so if they got out of step, the tests would fail:

And then I moved all these new classes into their own Eclipse project. And hey, waddayknow? It's a sort of teeny-tiny mocking framework (well, sort of). So I christened it JMicroMock.

Naturally, having just a handful of basic use cases, it's a very limited mocking framework. Much of the work is actually handed back to the Java language, in the use of anonymous inner classes. We can only mock what we can override. We can only match exact parameter values. We cannot set up more sophisticated expectations, like "This method should not be invoked."

And, of course, if we have to set up a large number of expectations, it will become very cumbersome.

But you know what? I can't help feeling these limitations might be a good thing, since they encourage me to limit the interactions between objects and to favour binding to abstractions.

Anyhoo, I don't expect - certainly, I heartily recommend you don't - folk to use JMicroMock for real. It's just an experiment, and probably fundamentally flawed in the implementation. Also, it has no tests of its own as a project, because it grew organically out of my original Video Library test code.

But it has been a useful reminder to me about how frameworks and libraries can evolve out of our everyday code. The use cases for JMicroMock were real use cases. I didn't make them up, I discovered them in solving a real-world problem and then refactoring out some reusable bits and bobs - the stuff I could use on other test code for other problems.

There's a lesson here about code reuse: in order for code to be reusable, it must first be useful.

It's also a warning about just how readily frameworks and libraries can appear out of nowhere. I solved the problem once, and quickly, but then spent twice as much time generalising one part of it. In this particular case, I've added heaps more complexity to my original code. It would need to pay for itself by being reused a fair amount, and that may involve dealing with new use cases for it.

Before you know it, JMicroMock is taking up all your time. I've seen it happen all too often - teams who were supposed to be building business applications devoting the lion's share of the effort working on some developer framework or other. But if nobody did, then we'd have none of these lovely frameworks.

The compromise is to restrict their use cases to what has value in the current project. So, maybe I could add support for more sophisticated parameter value matching (e.g., with Hamcrest), but if I don't need it, then perhaps I shoudn't. Let whoever has those use cases adapt the framework to their needs on their time.

Unless, of course, the plan is to keep development of the framework completely proprietary, in which case, you're on your own...

Posted 5 years, 3 months ago on August 2, 2015