Organizing maintenance 3: Testing
This is the third and final part in a small series on organizing maintenance activities. The other parts were about understanding (what to do) and modifying (the system). I actually feel bad about dealing with testing in the final installment since I don’t want to give the impression that the end is where testing belongs. As I’ve explained before, the waterfall model is a great model for explaining the various activities involved in creating software, but it’s a lousy model to base your development process on. Because of this, I’ll be discussing things that are relevant across all parts of the maintenance process.
Green-Red-Green-Refactor
I’m a big fan of automated testing. In fact, I’m a big fan of automating anything that has to be repeated a lot, because it saves you a lot of time and prevents small errors from slipping in. When being assigned a maintenance project and receiving the system to maintain, the best thing that can happen to you is that it comes will a full suite of unit-tests. Unfortunately, this is rarely the case. But since you want to automate testing as much as possible, the first step in maintenance should be to create some unit-tests. (See an earlier post for the types of maintenance.)
- For corrective maintenance, create a unit-test that exposes the bug or incorrect behavior. This is basically in line with what the traditional test-driven development techniques tell you to do.
- For perfective maintenance, first create a unit-test that tests some of the existing functionality related to the request (sharing some of the implementation for instance) and make sure it succeeds (green). Make a copy of the unit-test and modify it to test the desired new behavior that’s not implemented yet (red). The reason for starting with a test for existing functionality is because existing systems tend to be quite a bit bigger than the typical components you’re working on when doing initial development. Because of this you want to make sure the test you’ve written is correct and this is a lot easier when you can start with a related and succeeding test.
- Adaptive maintenance requires more testing: changing the environment is a pretty big step. The quickest way to test this is to create a small set of extremely large grain tests: together they should touch as much code as possible, so that you can quickly identify that there is a problem when porting code. The same is true for preventive maintenance.
In initial development the goal is to test as little as possible in a test so that a failing one not only identifies that there is an error, but also to a high degree of precision where the error is located. Unfortunately it’s rarely possible to create such a set for an existing system that doesn’t already have a large suite of unit-tests, so we have to forget about that aspect of unit-tests and accept that all they can do is validate that the system works. Which is pretty good in its own right.
Acceptance testing
As part of figuring out how to implement a change request or additional functionality, create an acceptance test for the modified system. This acceptance test should clearly define how the system should function after the modification has been performed. This is actually something that’s easier during maintenance than during initial development: when discussing functional requirements for a system that doesn’t exist yet with a user, it’s very difficult to make sure you’re both talking exactly about the same thing (for instance because of implicit requirements). In maintenance however, there is already a fully fledged and functional prototype: the current system! This means that you can talk about how the changes impact the current system and talk about what should be different instead of talking about how something should be that doesn’t exist at all yet.
Part of creating this acceptance test is also using the system yourself. I’ve seen a lot of maintenance work being doing directly from a requirements document or a bug tracker: developers that just browse to the code where the functionality is located and modify some stuff, not thinking about how the modification will impact the entire system and then cross out the requirement in the document or set the bug to completed in the tracker. This is a source for new change requests however, so it’s a matter of days or weeks before you find yourself confronted with the same or a similar change request. Instead, use the system, get an idea for what the user wants differently and after implementing it, verify that you’ve achieved this (using the acceptance test).