Every time I find it difficult to think of a good test to start, I am always tempted to try backdoors like getters, setters to help get started. The backdoors are not limited to getters and setters alone. Here is a list of my observations which has been used only for writing unit tests in Java easier.
- Equals implementation
- Protected or package private access specifiers for variables and methods
- ToString implementation
- Special backdoor methods to alter the state of an object (The worst of all)
Any line of code which exists other than for another production code, it encourages a programmer in the future to exploit that backdoor for quick gains. As the tests are the specification to the production software, it is better to resist the tempation to use any of these. It also causes increase in code length and also an excuse for more people to follow the same path. The more time and effort I had spent trying to avoid backdoors, I have benefitted with more clarity in design. The benefits have prompted me to have routine static checks in the code base to keep them at check.
A question and a counter argument posed by a peer who belonged to another school of thought. If tests are considered to be first class consumers of production code, then is it right to have some backdoors help ease writing tests faster? Comments?
An interesting statement/question. I find myself making some minimal design trade-offs in order to have testable code and cannot always see a way out of it. While I never have and don’t think I would ever build in test-only operations or state, I often have operations or properties with more open access than I would normally give. I am curious what sorts of design decisions you have arrived at where you were able to maintain the correctness of the code and yet retain test-ability around the backdoors you mention.
For me, this effect on design is one of the key benefits of Test Driven Development. Usually, an apparent need to access something “inside” an object during a test is a sign that the responsibilities could be factored better.
A typical example is the common question :- “how do I test private methods?” My answer to this is along the lines that a private method with significant behaviour might be better as a public method of a more specialised collaborator.
The same is true for “back doors”. If your test cares about an internal state, then maybe other clients might too. This might be hinting that it should actually be an external state of an injected collaborator. The collaborator can then more easily be mocked or stubbed in the test case, as well as allowing “real” client code to supply alternatives.