So what is all this talking about “automatic testing”? I know my code work so I don’t have to write some “test case” just to verify that; A complete waste of time!
Just kidding. No one in their right mind would say that they write bugfree code. One of the tools to help weed out the bugs before the code hits production is automatic testing, more specifically unit testing. This helps fix some problems but it also presents some of its own.
The dilemma I am constantly finding myself in is this: I want to make beautiful code and design, but I also want to make sure it is functioning as it should. Now, there are a number of more or less subtle ways to make your code less error prone and one is by making it as succint as possible, and writing it according to a specific design. That in itself is what we as programmers should strive for, because succint code that is written according to an easy-to-understand specification or design is also easier for other programmers to read, update and bugfix.
Another way to reduce the number of errors in your code is of course by using unit testing. This, however, seriously hampers good design – in fact it works directly against good design; Suppose I have made a class with a number of methods. All methods except one are private or protected since they only serve to partition the task of the single public method. I don’t want to write a unit test (some would call it an integration test) for this single method since it would 1) cause side effects in my database/file system, and 2) is so complex that writing a serious test would requiere days of work because of the data setup required by the method as well as the sheer number of possibilities for testing inside and outside the boundaries of the valid inputs.
Okay, but what if I just test some or all of the private methods that handles some specific subproblem? Well, I would have to make them public in order to test them. This goes directly against my design, as well as the principles of encapsulation: the unnecessarily public methods only tends to make the interface of the class more confusing, especially for any programmer who later looks at – or uses – the class.
Another example: I am writing a method that takes a number of inputs, do some magic to these inputs to produce new data and writes those to disk/database/network/… Clearly the persistency is of no specific concern here, so in order to test this I have to split the method into two or more and test those separately. And if I’m really “lucky” I have to use dependency injection. All this only adds more code and serves no other purpose – other than enabling me to test the code – than making the code harder to read as well as partitioning code that conceptually and by function belongs together as a single unit.
Then there is the all-pervasive question of how much. How much should I test? Some say that we need to test as much as possible. That presents two distinct problems:
- Should I really test everything? I write a lot of code that is so simple that writing a test case for it is simply a waste of time. And adding over 200% more (test) code is simply not justifiable to anyone who have an economic interest in the project. Besides, this just gives you a false sense of security as well as multiplying the number of errors in your test code.
- How many inputs should I test the mthod against? All possible? Well, some accepts an infinite number of input combinations. So how will I choose the right inputs, both valid, invalid and borderline? Again, this only serves to give you a false sense of security.
In my daily work I write unit tests, and I even use it occasionally in my private projects. But writing succint code that is structured logically is an even better way to write error free code as well as maintainable code. I really do not like having to make designs that resemble something I wrote in high school, and having my creativity hampered by… a tool. That is just wrong, and a lot of ugly code has been written on that account – code that is a nightmare to maintain, use and develop.
And what really makes me wonder is this: often I have heard the argument “writing a test forces me to think about how I would use this-and-that component”. Well, if you hadn’t thought about it before you started writing it you shouldn’t start at all. Some people even like to write the test before they write the functionality. I’m not one of those guys but still I have to write tests. Unit testing is often utilized in projects that uses so called “agile” development, but in my experience not everyone gets more agile that way.
Yeah yeah, occasionally I find bugs that I didn’t think about when writing a test. But more often than not I spend way too much time correcting an old test case to fit the new requirements than I do actually implementing those requirements.
I state that the way we write tests today are inferior and an almost complete waste of time – a hell of a lot of bugs are found by the users regardless, and my time is better spent writing correct, logical, succint – and by definition – maintainable code than writing all those tests.
I write tests, but I don’t have to like it.