I did move on to refactoring some of my stuff using TDD. Well kind of. In outline…

  1. I had a bunch of code which I wanted to redesign.
  2. This code wasn’t under test.
  3. The changes I am making are fairly drastic. No matter how hard I try, refactoring this code (versus rewriting it) is very hard.
  4. I couldn’t even write tests that would work with both versions (new and old)
  5. I’m refactoring this stuff ‘as best as I can’, knowing that I can’t avoid regressions.
  6. Once I have a half-cooked draft of the code using the new design, I write unit tests to help me finalize and validate the new code.

What would you call that. Test driven refactoring? Either way this gives me some elbow room, letting me reuse my existing code without overly tying me to the existing design. The whole process feels utterly painful at the moment, but that’s mostly because I have a release target and pushing it back is demoralizing.

It would probably feel a lot easier to just rewrite some of this stuff from scratch, with or without TDD and unit tests. Unfortunately my experience shows that there’s a huge lie factor in there. However painful, reusing old code doesn’t just mean taking onboard code that’s not shaped to work with my design. On the positive side, it forces me to look at code that actually does a job, rather than starting from airy, over-simplified abstractions.

Practical considerations

Even if you haven’t read my previous article about using/not using doubles, you may still be aware of the tradeoffs involved:

  • Using doubles means writing more test code (stubs, mainly; I haven’t found mock tools for objective C just yet). It allows covering the object under test atomically but leaves integration testing undone.
  • Using actual collaborators is typically easier if we have production grade collaborators handy and we’re confident that the code already works. Coverage isn’t atomic, so it’s like putting a foot into integration testing without altogether giving up on unit testing.

Practically the decision is made on a case basis. Some examples:

  1. If A uses B, B is easy to instantiate and I think B is correctly implemented, then I typically don’t use a double.
  2. If B is instantiated by A, then I typically don’t use a double
  3. On the other hand, if a resource is hard to instantiate, I’ll give up fairly quickly.

Either way, putting the classes under test typically involves redesign/more design. The redesign process I’m doing this for is already time consuming. So the short answer is, whatever works easiest is better, because trying to solve too many problems at once can result in a lot of confusion.

Nil stubs?

Objective C has this weird property that nil is a valid target for any function call (that call is just being ignored). So I’ve encountered a number of cases where instead of writing a stub or instantiating a collaborator, you can just use nil.

In development mode, I normally guard against passing nil using asserts. Feels like a very slippery feature that ‘lets nothing happen silently’ instead of failing safely and immediately. So I find interesting that nil has this hidden ‘testing friendly side’. I could write weird sh*t to allow nil when testing, but I feel rather worried I’ll end up with a lot of conditional compilation.

OCUnit templates in XCode

In XCode, the default class template for unit test is nice to understand how things work the first time around. It does become tedious after a while as it’s loaded with pretty much every other useless thing we don’t need. It’s pretty easy to find and edit templates from the XCode folder (take any significant code fragment and search in Finder)

Off topic

I guess ‘use whatever works’ isn’t very impressive advice. Unfortunately at the moment, ‘whatever works works for me’. If you have an inkling of what’s going on, bring your silver bullets and comment. Please don’t advise Working Effectively with Legacy Code. Like there’s a manual and you need a degree to drive a garbage truck. Just kidding, I’ll read it later.

In the meantime there is a very nice article about waste management on Wiki. Just so you know. Worrying about code metrics also seems a bit of a digression, notwithstanding I just found interesting bits here. I like code metrics.