Friday, March 2, 2012

So I was thinking about code reuse and testing and what the actual goal is from testing and came to the realisation that if you are testing for a certain value then you should hard code that in your test. Let me give you a small example.

Say you have a timed dialog, ConnectionError and the timeout on this dialog is 5 seconds. So you write a test that ensures this dialog times out in 5 seconds and all is good. Now say during the course of work other dialogs are created and you decide that they are all of the type 'InformationDialog' and shall all have a timeout of 5 seconds. Your test still passes and all is good. Now at this point it might be tempting to go in and refactor the ConnectionError tests so that it doesn't test for a timeout of 5 seconds, but for a timeout of InformationDialog.Timeout. This would seem logical as the ConnectionError is an InformationDialog. I suggest not doing that, and here is why. If you now go and change the InformationDialog.Timeout to 7.56 seconds the test will still pass. But...

By the test passing you have missed that you have potentially changed a business requirement. By the test passing you have missed the fact that your change in one area has affected something potentially unrelated. I thought tests were there to help show when you change something and it has an unintentional side effect. By the test passing you miss the conversation 'Is it important for the ConnectionError dialog to have a timeout of 5 seconds? It also brings about another interesting point, tests should not be resilient to change. In this case a good factoring has meant that even though you changed something the tests still pass, which is not something you want. You do not want tests to be resilient to change, in fact you want them to break as soon as possible to alert you to the fact that something has changed. Now I know you might be thinking that by hard coding tests and making them not easy to change will result in more time maintaining them, and you are potentially correct. But I would argue that anything that is testing a business requirement should be, so that you 'think' about what you are doing before you act. Couple that with making classes simple, and with less dependencies and the overhead will be minimal.