Last night Dan and I presented JBehave 1.0.1 to the assembled masses of Thoughtworks UK.
“Seriously though,” one spectator commented, “no one really does any of this AgileDocs stuff. Who actually reads the names of tests to find out what the code ought to do?”
On a project in which the tests consistently describe the behaviour of the code, the answer is: everybody.
“Okay. But why is it always so hard to write good tests?”
1. Your tests are not actually describing the behaviour of your code
I frequently see tests which describe the behaviour of a dependency too. A dependency is anything which is used by the thing whose behaviour you’re trying to describe. eg:
The driver should stop at the zebra crossing.
The driver should press the brake pedal at the zebra crossing.
The car should stop when the brake pedal is pressed.
2. Your code is wrong.
This happens a lot to me if I’m pinning down legacy code, and I’ve assumed that the method called
getTheOneRing() does actually do what it says it does. If I find I’m getting a copy back instead, there’s really no point checking that it’ll melt in the fires of Mount Doom.
3. Your code has too many responsibilities.
It’s probably doing something better done in another class; think about introducing another dependency (like a
4. You’re trying to mock too many classes.
See 2 and 3.
Don’t mock domain objects. Your code should interact gracefully with them, and be able to rely on their behaviour. Mocking domain objects leads to fifteen expectations per test, and gives you no better certainty that your code is behaving itself.
Don’t mock data structures (which are frequently domain objects anyway).
Split dependency classes which consist of mixed data structures and methods to manipulate the data. That way you only need to mock the manipulations. (As an example, consider the separation of Pattern Matching and Strings). Or, even better…
Use role-based interfaces to limit the scope of dependencies. Your code should perform according to its responsibilities; it should depend on roles which help it to perform those responsibilities. If a driver’s only responsibility were to
pressTheBrakePedal(), then a driver would only need a
Pedal, not a whole
Car or even a
Hydraulic Braking System.
A pattern I use sometimes
If I have a
Driver that needs a
Pedal, I might define it as a public inner interface of the
Adapting from a
Car.Brake to a
Driver.Pedal turns out to be easy, for most instances of Cars, Drivers, Brakes and Pedals – and if, later, you want to put in that super duper new parachute breaking system for jet-powered vehicles, you can do so and adapt to the Driver’s pedal requirements without changing the behaviour of the Driver.
In section 4. you say
“Split dependency classes which consist of mixed data structures and methods to manipulate the data. That way you only need to mock the manipulations.”
This sounds like breaking encapsulation to simplify testing. That does not sound like a worthwhile tradeoff. Did I misunderstand you?
Sorry, forgot to say above that It is a great post except for the bit I am questioning above.
“Encapsulation” is just a term to describe a consistent, coherent set of behaviour and underlying state. I certainly wouldn’t advise splitting any such set. On the other hand, I’ve seen plenty of behaviour that doesn’t sit well with, or doesn’t need to sit with, the data it’s manipulating or using.
For instance, I’ve seen things similar to Regular Expression Matching, placed in the same object as the String it was matching against (it wasn’t actually using Strings, just a domain object; but this should give you an idea of what I mean).
However… I’m glad that it’s easy to see how this would simplify testing. It’s only a small step from seeing how one might break away an interesting method, to using the role-based interfaces – a great way to ‘split’ classes without breaking encapsulation.
Does that make sense?
I added the example to make it clearer; thanks for the feedback. 🙂