So, here’s my response to Cashto’s post. I value unit-level BDD hugely, and it’s fairly intuitive for me. So I’m also going to try putting in some hints and tips about how to get this right, as well as some alternative opinions that I’ve come across recently which surprised me.
I’ll be mixing language here, so that I can respond to Cashto’s language without confusing matters. If you want to use the language of BDD, see my last post.
I’ve snipped bits of your blog, Cashto, because this is already going to be a very long post. Hope that’s OK. I’ve kept your sections in to make it easier for people to read.
(You can skip to the bit where I say it’s Okay not to write unit tests if you want.)
But Unit Tests Work For Me!
Mostly, yes. I don’t tend to mock out domain objects – I use a builder pattern instead. I’ve also written integration tests which talk to the database or file system.
I think of them as descriptions of behaviour, together with some examples which illustrate that behaviour. I’m less worried about whether they’re testing anything than I am about whether people can change the code safely and work out what went wrong, if and when it does go wrong.
If you can reuse units of code and and throw an exception when something is false, you’ve got a tool for the job. All the rest is just reporting, and making feedback faster.
When I learnt TDD, I was at that stage in my career where I didn’t quite get OO and good design. I was lucky enough to come across BDD fairly early in my TDD learning, and that really helped me realise that TDD isn’t about testing. Most of the confusion I can see in your post comes from that word, “test”. I value BDD because it makes me think about the responsibility of my classes, and where I can appropriately delegate other responsibility.
Unlearning is much harder than learning. Once a senior developer whom I respect hugely stormed out of the room because I found it so difficult to code without writing tests first, and it was slowing him down. Eventually we realised that I was using them to understand the context of what we were doing. After that I was able to contribute much more productively. If someone really likes their unit tests, they may be doing the same thing – just trying to understand your thoughts and write them down.
Training wheels are a best practice—for learning how to ride a bicycle. They’re not a best practice for riding the tour de France.
As I’ve become better at BDD, I’ve started becoming more pragmatic about where I write tests and where I don’t. On my toy projects, if a class can be tested by cursory inspection and it’s covered by something somewhere else – an acceptance test or scenario, maybe – then I might skip it. If in doubt, I tend to write the test. I’ll certainly write something if there’s someone else on the team who needs to understand why I wrote the code I’m about to write. I also use them to help me with the design.
Unit Tests Give Me Confidence When Refactoring!
And they’re too fine-grained.
Sometimes I find this is true. It’s not so painful, though, because the design that I’ve created through BDD is simple enough that my class is no bigger than my head, and has maybe four aspects of behaviour. It’s fairly easy to change that behaviour, and the full sentences that I use to describe what I’m testing makes it easy to see what old behaviour I should be changing.
I agree that any such test isn’t really useful. The only reason for having tests is to help you change the product – otherwise you might as well manually test it, treat it as fragile and never change it again. If tests aren’t helping make change easy, there’s something wrong.
Unit Tests Catch Bugs!
Oh, yes. Don’t do that, people. The rest of the team are bright, intelligent people; if you give them a chance to understand the code you just wrote, they’ll use it correctly. Writing tests to describe why your code is valuable and how to use it can really help here.
Try pair-programming, and getting one person to write the tests which the other person codes. I’ve certainly blinded myself to my own bugs on my toy projects before. Fortunately my automated scenarios or my manual testing tend to catch this.
Absolutely, which is why experienced, professional testers are so valuable. They think of all the scenarios we missed. They behave like users determined to break your app. I love those sneaky buggers.
They do help me fix them more quickly.
Unit Tests Improve My Design!
I keep seeing this. I even saw someone do this in Programming with the Stars. Don’t do this! Write an example of how you’re going to use the code. It’s not about testing individual methods; it’s about the behaviour of an object as a whole. If you need more than one method for a class to be valuable, show how you’d use them in conjunction.
Of course, if you’re writing tests first, you’ve got the interfaces because you have no idea what’s going to implement them, so you’re completely independent of the implementation. Imagine it’s all done by pixies.
TDD as the Cujo of the Agile world…
I do sometimes find I have to revisit the way I’m using my collaborators, once I’ve actually tried to use them from the real code. This is the only time I’ve found that encapsulation breaks; I’m dependent, at least a little bit, on the implementation. I’ve not found this to be much of a problem, though. More modern, BDD-style mocking frameworks such as Mockito (Java) or Moq (.NET) also help to keep things easy to understand and change.
I hear this too. I’ve been trying to learn languages like Haskell. Maybe I should put more effort in here.
If You Can’t Test It, You Can’t Ship It!
For some people, in some contexts, it’s okay not to write unit tests. I’ve spoken to a few people who haven’t yet learned to write unit tests, but who are writing excellent code, usually with automated scenarios that test the whole system. This includes one better known member of the Kanban community, who’s been delivering code quickly and successfully for years. I certainly won’t sneer at any team or individual who can deliver working software while enabling change, however it’s done.
Rather than discouraging people from writing tests at all, I’d encourage them to look at how to get the first kind of tests instead of the second.
But It’s The Corporate Standard!
Sorry to be so blunt, but there it is. If it’s any consolation, a lot of organizations have the same dysfunction: they don’t trust their employees to do the thinking. They think they know better than you, and they don’t.
Or, they may recognise that people in their organisation are still learning, and are putting these standards in place to ensure that the team have the space, time and motivation to learn. They may also recognise the value of tests in documenting code, allowing them to move developers between teams more easily, bring in new people, etc.
Oh, yes. Don’t do that. Question everything, challenge everything – but try it out first.
So What Are You Saying, Man?
Possibly. Unit testing isn’t actually about testing, though. The word “test” is such a misleading term; it’s not just about finding bugs. It’s about preventing the bugs from happening in the first place.
The closer to production you get, the more important the feedback loops are. The most important feedback loop of all, of course, is putting the code into production and seeing if it’s as valuable as you think. (I’m learning about other important reasons for doing this too.)
Do try to make sure that the rest of your team can find value in your code, understand how to use it and change it safely.
It did. These are problems with TDD that I come across frequently. Pretending they’re not there isn’t as useful as addressing them, so I hope my response has helped too. Thanks, Cashto.