Debugging and refactoring go well together.
I spent the past few days bug hunting near the end of a release, and I took the opportunity to address all the todo comments as well as buggy behavior. A task that started off as simply removing a library that wasn’t being used anymore lead into a more serious refactoring of the database interface that exposed a few latent bugs.
During the long build cycles, I again tried to go through the Learn You A Haskell For Great Good, but I found that I just wasn’t absorbing it without the ability to test it out. I’ll need to keep my eye open for a project that would fit well with a functional language.
While fixing bugs, I often found it tempting to do general refactoring of the class to help make it more testable. Most of the classes had some tests, but when I tried to make a test to prevent a regression of the problem I found, it was very difficult. The parts of the classes that were tested were the trivial parts, and they didn’t even run fast (contributing to the painfully long build cycle that started this diversion)!
I then happened upon a youtube presentation that caught my interest by the author of Angular.js on the psychology of testing. He brought up a great point, everyone says they like tests, and no one would admit that they don’t know how to write testable code, but yet new code continues to get written with incomplete unit tests.
My experience with TDD has been when writing a utility package or library, something sufficiently isolated and well understood that enumerating the expected inputs is pretty simple. When interfacing with legacy code, unit tests quickly become more complex as the boundaries blur. The most common argument I’ll have with myself is “what exactly am I testing here”. Because sometimes I end up writing tests that touch and indirectly test all the dependencies of the object, as well as the object (I’ve had many discussions with my team about what boundaries to draw on tests when working with Angular.js controllers). Angular’s use of dependency injection never struck me as the key to it’s testability, as I’d never been exposed to DI before Angular (not being Java or Factory pattern user). Separating the program’s construction from the program’s business logic sounds like a good idea, but I haven’t seen or attempted it in practice. I’ll have to keep this is mind when putting together new subsystems in the future!