I spent last two weeks deep diving into code written by our contractors and writing some test against the same. This was a pleasant break from my regular duties of an architect. As an architect I am always trying to ensure the code follows pragmatic design principle and I really dig SOLID and TDD.
Coming back to the subject at hand, here’s a summary of the two weeks.
I spent week one reviewing, writing test cases and refactoring a new application. This application is written on Spring Framework with a bit of secret sauce. It adheres mostly to the SOLID principles but has the tests all wrong. They violated most unit testing ‘laws’. Long story short, they were integration test and not unit tests.
Created mocks and stubs, injected these into the container and then wrote unit tests using the mocks. After the exercise, I was happy with the removal of all test dependencies and that the tests ran blazing fast. As an added benefit, I could now see the code coverage and compare them build on build.
Following this, I trained the contractors. Gave them a brief overview on unit testing the right way, and then showed how it could be done. Did a pair programming session and helped them write some tests.
Buoyed by success of week one, I decided to attack the big hairy monster. This is a J2EE application with myriad dependences. DB, JMS, RMI, Tibco Rendezvous; you name it, you got it. The application has grown organically for 10 years and developed by multiple teams. The unit tests take 2 hours to complete! Mostly since they aren’t unit tests at all.
As advised by Michael Feathers in the book “Working Effectively with Legacy Code”, I started by writing tests and breaking dependencies.
I was successful in breaking dependencies, but I had to perform some nasty hacks. Since the classes were not open to modification and depended directly on the J2EE container I had to mock the container. On the bright side, since EJB 1.1 and 2.0 required certain interfaces, I could easily mock the Home classes and write dummy implementations where needed.
Then I hit a million line tall code wall. This God object knows everything and does everything, and I mean everything. This is an ugly artefact in an otherwise well written application. It violates every single design principle and has a directory of who’s who of anti-patterns. And the methods in the class are to be called in a particular order, else you will invite God’s wrath. The methods do more than they advertised, do not throw or return errors, least cohesion, and what not.
Due to this, the tests I wrote can only validate a complete workflow and not individual steps. At least the tests are not dependent on external factors and run fast. The next task on my plate is to refactor the god object into smaller classes that follow the SOLID principles.
To do so, I am following the advice in the book “Working Effectively with Legacy Code”.
Please share your advice, tips and experiences on the same.