Lastly I am flipping the bit and diving into TDD and I must say - this is quite an adventure. At the beginning I was like a babe in the woods. It was not easy to start. Everything was harder - writing code, designing, thinking... I had to firstly write a test, and then the code - everything was upside down - I didn't even have the code to test. Damn! I started to think about basics again, and that... was really refreshing!
One day I was given a task to figure out changes in history lines of some objects and update the meta-data for each line accordingly - with ids of objects that were changed. For the simplicity of the example let's assume, that those objects were rectangles and I was interested only in their length.
Let's use an example:
The task was not that hard and my very first thought was to create some meta-data generator that would accept a list of Lines and update each of them. Sounds easy, doesn't it?
If I didn't do TDD, I would go and write the code first - iterate through the Line list, then in each one iterate through all rectangles and for each of those find matching one in previous Line. Then I would compare them and update the meta-data. Plus some corner cases - handling one Line only, or something similar.
It would probably take me some time to write - iterate through two collections, find matching rectangles in previous Line, and then the comparison. After that - I would write tests - prepare data set up for the scenario - a lot of objects, a lot of mocking, and some verification. Probably this test would be quite big. Enough to say, that my objects were much more complex, than simple rectangles. ;)
However I tried to write the test first and it was just a pure pain... Creating so many rectangles, mocking so many other involved objects and thinking about the future code that I was going to write... This approach was just not testable at all... I took a break.
After couple of minutes I returned to the problem and started to think what was wrong. I figured out, that there were just too many responsibilities in that not yet existing code. Single Responsibility Principle was broken even though not a single line was written. That made me thinking.
After short time I discovered some implicit concept - a Comparison Pair. I was trying to compare whole Lines, where I only required comparing pairs (A1-A2, A2-A3, B1-B2, B2-B3, C1-C2, and C2-C3). So I started to change my design in my head.
First - I had to split those Lines into Comparison Pairs, and then for each of those Comparison Pairs generate the meta-data. I created interfaces for those two responsibilities and mocked them in tests, which were simple, readable and were covering all paths. My generator was just delegating the work to those interfaces.
Then I created tests for splitting Lines into Comparison Pairs - again smoothly - followed by implementation. And the same for comparing those Pairs.
So they should, or should not?
And we are back to the original question - should tests influence the production code? If you are doing TDD, then you don't have a choice. Your tests shape your design - just like in my example.
If you are not doing TDD and writing your code first, then you may encounter some problems with testing it later. (Of course if you are not writing tests, then you won't have any problems - right? ;) ) When the code is hard to test, you can do one of following things:
- give up and do not test
- do the nasty work and write this huge and unpleasant test
- add some helping methods in the code with this "awesome" annotation - @TestOnly
- refactor the code so that it would be testable
So to summarize - I think, that tests should influence the production code. They make the design better. They are helping with finding implicit concepts and following SOLID principles. Do you agree?