On why we test (code)
I’m not a great programmer; I’m just a good programmer with great habits.
Last week I had a talk with our lead software testing engineer, on multiple aspects of what quality in software development means. Even though it is just a cog in the grand scheme of quality assurance, maintainability of the code base strikes me as one of those aspects for which the responsibility is entirely on developers. Automatic tests, refactoring and diligence of the code reviewers are the only safeguards of the code quality. Other team members, testers and managers, will not be able to help us and some will through external pressure (deadlines and such) inadvertently contribute to its decay. This brings us to the subject of…
Legacy codeMichael Feathers introduced a definition of legacy code as code without tests. When present and adequate, tests give us the confidence to make changes to the code without fear of regression (breaking something which worked). You can only fully appreciate this if you maintained a code base written by someone else (quite possibly a group of people which is not available to you) over several years. The key problem of legacy code is that it was never designed to be testable. So before writing our safety net of unit tests, we need to refactor the code to make it more testable. However, in order to refactor safely, we must have unit tests to verify that we haven’t broken anything while making changes…
Advice and techniques on how to deal with it, you can find in Michael’s book Working Effectively with Legacy Code.
I will be focusing more on how not to write legacy code.
Test firstThis is a well known technique of the practitioners of the TDD. The first step is writing a failing test. Yes, you write test before you write the production code. Why the f… would I do that?! – Because of the proper focus. Before you start coding, you can actually focus on the task your code needs to do. Getting invested with the technical details of the implementation may impair your vision of the place and purpose of the new code. Testing first will give you a perspective of how your code will be seen from outside and how it fits and collaborates with its surroundings. Writing the tests after usually means confirming that the code does what you wrote it to do. This is a valid goal itself. However, we may be missing the insight we get while thinking about the problem from ‘client’ perspective. This insight could be integrated in the design and implementation of the solution. Other danger is that being invested in the solution, one might tend to compromise the quality and role of the tests, as being seen as something which just confirms something we already know (that our solution is good and it works).
One of my managers asked me once :
– How many bugs did you find with the unit tests?
– None – unit tests are there to prevent bugs from ever entering the production code.
Testable codeBeing able to produce good tests means that:
- You understand the problem you are trying to solve
- You have a good understanding of how your code needs to interact with other parts of the system
- You will produce a testable design
– To a point. From my experience, being able to produce the testable code most often dictates:
- You have removed tight coupling where it is inappropriate
- You have reduced the complexity enough be able to cover all relevant states and behaviors of your code to prove it is working as designed
- I will add stuff to this list as it comes to me