Certain practices and disciplines must be observed and adopted by software experts to be able to write testable codes.
Every developer knows that the testable code can make life easier. There are a lot of books and articles written about unit-testing. Particular attention is paid to Test-driven development (TDD) as the best process for the development of hi-tech products.
Unfortunately, developers may ignore them to benefit from time saving or because of the lack of competence. Anyway, situations, when you need to write a test for a fundamentally untestable code, are more frequent than might be wished.
Some Tips on How to Write Testable Codes
Mixing object graph construction with application logic
In a test the thing you want to do is to instantiate a portion (ideally just the class under test) of your application and apply some stimulus to the class and assert that the expected behavior was observed. In order to instantiate the a class in isolation we have to make sure that the class itself does not instantiate other objects (and those objects do not instantiate more objects and so on).
Most developers freely mix the “new” operator with the application logic. In order to have a testable code-base your application should have two kinds of classes. The factories, these are full of the “new” operators and are responsible for building the object graph of your application, but don’t do anything. And the application logic classes which are devoid of the “new” operator and are responsible for doing work. In test we want to test the application logic. And because the application logic is devoid of the “new” operator, we can easily construct an object graph useful for testing where we can strategically replace the real classes for test doubles.
Doing work in constructor
A class under tests can have tens of tests. Each test instantiates a slightly different object graph and than applies some stimulus and asserts a response. As you can see the most common operation you will do in tests is instantiation of object graphs, so make it easy on yourself and make the constructors do no work (other than assigning all of the dependencies into the fields).
Any work you do in a constructor, you will have to successfully navigate through on every instantiation (read every test). This may be benign, or it may be something really complex like reading configuration information from the disk. But it is not just a direct test for the class which will have to pay this price, it will also be any related test which tries to instantiate your class indirectly as part of some larger object graph which the test is trying to create.
Favor composition over inheritance
At run-time you can not chose a different inheritance, but you can chose a different composition, this is important for tests as we want to test thing in isolation. Many developers use inheritance as code reuse which is wrong. Whether or not inheritance is appropriate depends on whether polymorphism is going on. Inheriting from AuthenticatedServlet will make your sub-class very hard to test since every test will have to mock out the authentication. This will clutter the focus of the test, with the things we have to do to successfully navigate the super class.
The key to testing is the presence of seams (places where you can divert the normal execution flow). Seams are essentially polymorphism (Polymorphism: at compile-time the method you are calling can not be determined). Seams are needed so that you can isolate the unit of test. If you build an application with nothing but static methods you have procedural application.
Procedural code has no seams, at compile-time it is clear which method calls which other method. I don’t know how to test application without seams. How much a static method will hurt from a testing point of view depends on where it is in you application call graph. A leaf method such as Math.abs() is not a problem since the execution call graph ends there. But if you pick a method in a core of your application logic than everything behind the method becomes hard to test, since there is no way to insert test doubles (and there are no seams). Additionally it is really easy for a leaf method to stop being a leaf and than a method which was OK as static no longer is.
Testable code is code that makes automated testing quick, easy, and enjoyable. Hence, you must learn how to write code that is easy to test. These skills are necessary to refactor code that is difficult to test into code that is easily testable.