In recent years, TDD has surged in popularity. If we look beyond pure TDD to consider writing unit tests in the same general timeframe as the production code, or having automated integration tests then such practices are even more widespread. However, I think that there is a bit of an echo chamber effect that happens with the sort of devs who blog, tweet, go to conferences, talk on podcasts, post on stack overflow etc. that can lose sight of how many people code for a living but don’t pay attention to what is happening in the world outside their office, so these good practices aren’t nearly as widespread as some of us might like to think.
I was brought into my current job as part of a drive to improve on the use of agile techniques and best practices. On a dev team of around 10 people there was only 1 other person writing unit tests so it all started as something of an uphill battle. Being the new guy on a team and trying to change the processes can seem like you are just being critical and accusing other developers of not being good enough. Introducing ideas with a good explanation of the benefits is vital to avoid causing an us and them type rift, even when it is part of your job description. When testing has been viewed simply as a manual process that is the domain of the QA department I think there are 3 stages of understanding to get across to the team. Firstly the value of having automated tests at all, with the implication that the suite can include integration tests. Secondly the point of having proper unit tests so that different components are tested in isolation. Finally the reasons for writing your tests before the production code.
Why automate tests?
- Regular feedback to show if changes have broken anything.
- Confidence to refactor code from working but messy to working and clean.
Why unit tests?
- Integration tests can be slow to run if there is network, database, filesystem access etc. Isolating your tests can provide huge speed ups with doubles providing required interactions. The regular feedback mentioned above can become even more regular.
- Taking other components out of the equation makes it far more obvious which part of the code is at fault when something breaks, an integration test could give a huge stack of components that participate, but a unit test will likely be a single public method call on a component with just a few private methods to dig through too. This can aid with the point about refactoring made earlier, although integration tests are essential if you decide to rework larger chunks of code.
- Mocking out the dependencies of a component means that different devs or even teams or companies can be working on those layers independently without holding up consumers of that code, as long as a suitable interface has been designed and agreed upon up front. Or even if it will be done by the same dev they can stay focussed on what the current class deals with whilst that is in the forefront of their mind, and move on to the dependency and deal with that later.
- Writing pure unit tests requires the code to be written with an eye to testability, meaning that code will have to be decoupled and conform to good design principles.
Why test first?
- Having the test in place before production code helps to push the decoupling aspect harder than writing tests afterwards where code may need changing to become testable, or the odd integration test may be allowed to sneak into the suite.
- You only write production code that a test has called for which helps you apply the YAGNI principle.
- That last D in TDD is Design not Development, writing your test first means that you are making a declaration of how you want to use that API so tends to lead more towards a clear, usable component. This is an area that is a bit more obvious with newer test frameworks that use variation on the word Specification in their name, rather than Unit or Test.
The first set is an easy win, the latter sections require more work, not to mention some fundamental changes in the way that the team has to approach their code. Fortunately we’ve had a push from the top level management to improve the number of tests that we have which is helping to push some of the more reticent devs into trying to adopt these practices, as well as giving us some scope to try and provide some mentoring to them so that they can start doing some TDD without being thrown in at the deep end.
Sadly we have some highly test-resistant code with lots of classes full of static methods that can’t easily be mocked out, so I shall post about the techniques that we’ve come up with for dealing with that soon.
No comments:
Post a Comment