In a previous post I talked about working on a C project on which I was given the flexibility to add unit testing and the tools I chose to use to do so. In this post, as promised, I plan to tackle how to convince a team to take the time to test when they’re not used to doing so. The first step in convincing a team to adopt unit testing is the general argument for why unit testing will be helpful. After presenting this argument, we’ll address the benefits and concerns applicable to each of the typical roles involved in software development. Further arguments against unit testing are answered in the post “Addressing Arguments Against Unit Testing“.
General Benefits of Unit Testing
In existing code bases without tests (legacy code according to Michael Feathers) tests act as documentation of the code as it presently exists. Each test provides an example of what the unit under test will produce given a certain input. Writing these tests as documentation will highlight areas where there is a difference between the common understanding of what is supposed to happen and what actually is happening. Additionally, a new developer (or anybody else who would want to read the tests and code base) can get up to speed quickly by reading existing tests. The process of writing tests for existing functions will often force changes to make the code easier to test. This comes up as you plan your test and realize one or more of the following:
- The function requires a LOT of input data. Possibly more than you thought was necessary given what the function is supposed to do.
- The output values of the function (which you’d like to assert are correct) aren’t where you expected them to be and you have to figure out how to access them to assert their validity
- The function is producing more than one output or calculating more than one value.
Changing existing functions to make them easier to test without changing their behavior is my current working definition of refactoring. In the process of making functions easier to test they will often become simpler, easier to understand, more predictable, and easier to continue writing tests for. Additionally, as the portion of the code base covered by unit tests increases and the refactoring necessary to implement those tests happens, you’ll find that larger architectural changes / refactoring will become easier and more feasible. Patience, however, will be required as it will take a non-trivial amount of time for the fruits of the testing labor to come to fruition in an existing code base. Each test that is written, refactoring that is done to make the code testable, and bug found and fixed as part of testing all take time to complete. The benefits, however, will grow in the code base like a good cancer (ok, that’s morbid but it’s the first image that comes to mind to describe the spread of tests and healthy code). The arguments that I’ve presented above are those that have been important in my current context. There are, however, more benefits to using testing that I will let others who’ve written about the subject describe. I linked to this blog at Apium Hub in my previous post as a starting point for why unit testing is valuable. It is a good and short enough read that I’m linking it again. Another longer article on the benefits of unit testing with lots of examples is this article at TopTal.
Addressing the Common Software Engineering Roles
Addressing Project Management
Project management is responsible for getting the project out on time with the features that the business needs. This is a hard job in the best of circumstances. With a code base that is complex, hard to change, and without tests this job becomes even harder because changes will likely take a lot of development time with a lot of QA work to ensure that the changes are correct without unwanted side effects. To add the request that developers take more time to add tests can seem like a bad investment of time. Especially if yet more work will be found in the form of refactoring; work that isn’t directly related to the project but will eat up time.
The benefit that will matter most to project management is the one that requires the most patience. That is that the code base will become more stable and easier / faster to change. This benefit, however, requires an investment of time up front to get the initial testing and refactoring done. This ends up being like a drastic change in diet; it’s painful at the start and you may not see that many benefits quickly but you know the change in habit needs to be permanent to work. You have to keep reminding yourself that it will bring great benefits over time.
Many developers I know who work on a code base that has been around for a while have seen some code they wish they could fix. Most of the time this boils down to a “fix” for a bug or feature having to be overly complicated because of the way the current code is organized. This leads to a desire to refactor the area in which work is being done. In my experience trying to buy time to do some refactoring ends up with one of two responses:
- You’ve picked too much to refactor. It will take too much time to complete the development work and touches so many areas of the code that the QA work will be off the charts.
- You picked a bite sized amount to refactor but what are the benefits to the project to doing this? And it touches another large area of code and, even though it doesn’t change much, we’ll still have to do LOTS of regression testing for that.
Once unit testing and a certain level of refactoring has been approved developers still need to respect the deadlines set by project management and not go crazy trying to test or refactor EVERYTHING. My suggestion is to create tasks in your task management system for any refactoring work you’d like to do that isn’t small in scope and time. This allows for you to lay out a plan for what you’d like to change over time and gives other developers a chance to review it, gives QA a chance to address how much testing it will cause, and gives project management a chance to address how it will affect the schedule. This will take more short term time and effort but it will force you to think about the design of what you’re changing and make a good argument as to why it should be done now.
I didn’t plan to address the impact of unit testing on QA folks. Mainly this is because the QA staff that I’ve worked with have had a knack for figuring out how to break my changes and find things that I was too close to the code to see. If I can knock out some of the trivial bugs by unit testing so that they can focus on finding the more complicated problems, so much the better.
If there is something regarding unit testing that QA folks are concerned about, please let me know!
Handling Tasks To Be Done Later
Just a moment ago, I recommended to developers that they track the refactoring that they want to do so that those plans can be vetted by the rest of the team. The team must be willing to handle grooming and planning to implement the refactoring tasks that are generated. If some of the tasks don’t fit into the schedule of the current project, there must be willingness to implement these changes at a later date or in later project. Having a process that enforces review of the desired changes on a regular basis to see if they’re still valid and if they make sense to work on would be of great help.
To wrap things up I want to acknowledge that starting to test on a project that exists without tests is HARD. It will be lots of extra work at the start. But, if you’re working on a product that will be with you for the foreseeable future, it is work that is worth the time to get right. Please resist the temptations to either skip the testing because you’ve been “ok” without it for years or try to re-write the code base because it would be easier to design with tests from scratch.