As developers, we all know that we’re supposed to test our code. We should write unit tests and yet usually this is the first action that we skip when we’re running out of time.
As team leads / managers, we all know that tests are necessary, but when a deadline nears, we tend to put less emphasis on testing, and more on cranking out code.
So there seems to be some tension in the testing field. We all know that testing is good for us, but yet we don’t do it anymore once there is some stress on the project.
Why Should We Test?
Edsger W Dijkstra : Program testing can be used to show the presence of bugs, but never to show their absence!
This means that testing doesn’t give you a 100% guarantee that your software has no bugs. But it helps a lot. We could turn this around and say that we have an almost 100% guarantee that there will be bugs if we don’t test, unless of course you’re typing over a version of a “
hello world!” program. And even this program you will test, because once you have typed in your code, you’ll be curious to know if the output will indeed be “
And with that, we’re on the first form of testing, that we all do: manual testing. We wrote the program, and then started it to verify the results. With a simple “
hello world”, this can be sufficient, but once there is some more complexity involved, this will result in a waste of time. This is also something repetitive, with a known set of actions and outcomes. Isn’t this why we invented computers in the first place?
In the case of our “
hello world”, this isn’t a big problem, but when you create a web application, where you want to test certain conditions that only occur after going through 10 pages, clicking some buttons and entering a lot of fields with (correct) data, you can see that automating this will be a huge time saver. If you can get a test runner to execute directly the function that you want to test instead of having to spend half a minute to get to that function, you’ll get huge savings!
But that means we need to code more. And coding more takes more time and effort. So it will take more time and our project will complete slower.
Or Maybe Not
Let’s create a console application to calculate the greatest common divisor (GCD) of two integers. There are many ways to tackle this problem, but for simplicity, we will:
- enter 2 integers
- calculate the GCD, with whatever algorithm you fancy
- show the output
Let’s go through the normal development cycle. We typically write a
main( ) function that gets the 2 integers, calls a function to calculate the GCD and then shows the result.
Testing. Entering the 2 integers in the console will take some time, and become quite boring if you have to repeat it many times to get your code right. It is also very easy in a console application to enter something wrong, causing the program to crash. This means that you have to restart the program, enter the 2 digits again, and then verify the result again. Mind that we’re talking about a console application that only takes 2 input values, no clicks are necessary (as in a web application), and already we see that this will take some time.
We then probably will want to test some more values, which means restarting the program, entering the 2 digits (correctly), testing, … So we see immediately that this will not be done because it takes too much time. Edge cases will be forgotten, and errors will only be found in production!
Also, when we change something, we need to run all the tests again (manually), with a high risk of the tests being forgotten, or shortcuts being taken.
There will be no trace of our testing effort. No log files are written, unless you add this job to your list of things to do (manually) during testing.
Negative Feedback Loop
Typically, when projects are delayed (for some reason), they get in a negative feedback loop. Or sometimes, we just decide to skip writing tests in the first place. This brings us to the following diagram:
The project gets delayed, so we must produce more (code). So instead of wasting our time with testing, we just develop, develop, develop. As a result, the code quality will be lower which will result (sooner or later) in rework. This rework will usually become urgent at the worst possible time (some people say that Murphy was an optimist!). Because of the rework, nothing else can be done, so the project will be even more delayed now. So weird enough, the more we code, the later we’ll finish the project. As a reaction, often more developers will be put on the project, only feeding the negative loop.
The lack of testing will inevitably mean that more bugs will be detected by the customer, in acceptance and production environments. So the customer’s trust will quickly drop, which will result in negative feedback. This feedback gets to the (already overworked) developers, who will get into “developer fatigue”. This causes motivation to drop, developers to leave, … So the project will be delayed even more.
Breaking the Negative Cycle
I think that you already have understood that there is a way to solve this. Let’s draw a different diagram:
We can start in the “project on time” bubble. We develop our code, and test it immediately. The tests are preferably automated (coded), so that they can be executed easily and efficiently. We have a tight integration between developing and testing, so this loop can be executed quite fast. When the loop is finished, we can be certain that the code quality is high enough; because it has passed the tests. Also less bugs will be detected by our customer which will make sure that the customer keeps his trust up. And if they do discover a bug (which still is probable), then we can adjust our test set, so we avoid recurring bugs.
Thanks to this, no rework is required and the project can move on.
When our project is already delayed, it will take some time to switch to this methodology. A feature freeze may be necessary. Don’t write new code, but start writing tests for the parts with the most (annoying – visible – expensive) bugs.
In this case, it doesn’t make sense to start writing tests for your whole code base. Some parts of it will be OK, so don’t waste your time there. But do write tests for the other parts. When I come in a project at this stage, I try to find the worst problems (prioritize), and then write some tests there. Then it becomes easier to “quickly” correct the code, and keep it correct while other areas are tackled. Because automated tests are run often, the risk for recurring bugs is reduced. And so, we can start to stabilize the project efficiently.
Often, this will also require some refactoring of the code, to make it testable. I’ll get to that in an other post.
In most projects, a balance will be sought between writing tests and writing code. But I hope that it is clear that testing is a great accelerator for your project, and not a waste of time.
In the next article, I will take you into the world of Test Driven Development, so you can become even more productive!