Why would I care to do that?
I hear this question a lot, not just about design but testing in general. The answer is simple. If I can assume, for a moment, that we are striving to be professional; then the reason for designing our code with testing in mind is so that we can ensure it has the least number of possible defects. Every test written is one or more defects the system has been hardened against. Fewer defects equates to a higher quality system.
Why can’t I worry about testing after the code is written
There seems to be a debate over Test First vs. Test After development. I don’t see this as a valid question. Testing the code after it is written is far more cumbersome than testing the code as it is being written. By testing the code first, you ensure that the code can be tested; on the other hand, however, testing the code after it is working will almost certainly cause some refactoring to enable some of the tests to be created. Refactoring takes time. Time is money.
In Test First development, we do a lot of refactoring but it is generally on a very small scale and only scoped to a couple of tests. In Test After development the refactoring could and often does encompass many tests. This type of refactoring also encompasses the tests you have yet to write. This raises the chances that the refactoring will break something, causing much more work in the end.
What makes code testable?
In order for code to be testable, it must follow the SOLID principles. Specifically, the most important SOLID principles to achieve testable code are Interface Segregation and Dependency Inversion. Following just those two will produce testable code. Following the other three will produce much more easily tested and refactored code.
As part of Interface Segregation, put all third party utilities and libraries behind an interface that you design. Convert all third party models, classes, and objects into data transfer objects and classes of your design using the adapter pattern. Doing this will make that third party completely replaceable for testing and other future purposes. If you are using C# third party includes the .Net framework. Let me say that again, just in case you didn’t see it the first time. If you are using C# third party includes the .Net framework.
Dependency inversion, to me, carries a lot of meaning. For now, let us assume the simplest interpretation and just make sure we avoid the new keyword in the body of our code. The only thing that should be creating objects is the dependency injection framework you decide to use, or a factory that gets configured in main.
Write Testable Code
In short, use the SOLID Principles and write code Test First. I hope this post has been helpful and will provide answers to the why and how of designing your code with testing in mind. As usual, please leave a comment below and feel free to ask any questions. I will do my best to answer and look forward to anything you might have to say, complement, criticism, or otherwise.