Is TDD worth the extra effort? I got a reaction from one iOS developer who began applying my tips.
Andy Dwelly began applying my old Objective-C TDD screencasts to his iOS coding. Here’s what he writes in Some notes on Test-Driven Development:

Improve your test writing “Flow.”
Sign up to get my test-oriented code snippets.
At first, progress was almost painfully slow.
Yup. It seems like there’s a lot to learn. The real barrier is that there is a lot to unlearn.
When you first start applying test-driven development to real code, your productivity will take a hit. This is totally normal. But if you’re willing to press through the learning curve, your productivity will increase again, in ways you haven’t experienced before.
TDD: Immediate Benefits!
I would normally expect to write the final line and then fire up the debugger to get it to actually work.
Except that it did just work.
I experience this fairly regularly, even when I TDD a new view controller.
I think at this point if I’d been asked anything about the issue of code quality I would have claimed that the actual code produced was marginally better. My TDD code certainly handled errors and failures better but had poorer information hiding and some dependency injection that I would not normally have bothered with.
Then my tests found a bug that I don’t think I would have spotted until the code hit the field.
Do the math: What is the cost of fixing a defect vs. preventing a defect? Even though TDD takes more time up-front (when it’s just your time), it reduces time later (when more people are involved).
I think there’s enough evidence out there to make this claim: Once you make it over the TDD learning curve, your time-to-market will be the same—but with many more benefits!
Once you make it over the TDD learning curve, your time-to-market will be the same.
TDD: Limitations?
I’m still of the view that trying to do a lot of UX tests using TDD is hard.
What Andy hadn’t yet experienced is the real powerhouse that TDD enables: refactoring. To me, this is the greatest benefit of test-driven development. It’s a shift of emphasis, from ”prevent bugs” to “empower change.” And that’s why I’m vigilant about unit testing view controllers, down to the nib connections. Where some people say, “I don’t think that last bit is worth the effort,” my reply is, “It’s often not that hard. Why would you turn down the chance to check your code automatically?”
Andy was still early in his iOS TDD journey when he wrote up his experiences. But it didn’t take long for him to experience some of the benefits! Go read his account.
What are your questions / concerns / reservations / experiences with TDD? Leave a comment below.
Ah, I’m not the only one :-D
I had to add a new feature to my existing project at work today. I had already decided to create a whole class dedicated to this feature. (Based on some of the ideas I’d seen and learned in Graham Lee’s book).
As I was about to start it I stopped and though, “This is a perfect candidate for TDD” and so I started the waltz.
It isn’t finished yet but it is well on the way and I already feel more confident about the outcome of it than I would have done without TDD.
The main difference (as you mentioned in your UI TDD screencast) is that I don’t have to navigate through a maze of UI to get to the new feature. The tests bypass this entire process and I can the class without even having it implemented in the production code yet.
It is slow, it is difficult (at the moment) but I will persist and get through the learning curve.
Thanks
Thanks for sharing, Oliver. And keep it up!
I’ve been messing around with TDD and Android, in which I guess one finds similar issues as in iOS. I’ve mostly found the following: (1) mobile platforms provide another framework to work around, (2) Learning Tests help me understand how parts of the platform and its libraries work, (3) with TDD I can steer in the direction of running the simulator as little as possible, (4) the visual aspects of mobile development are more important than in other contexts, and automated tests still don’t help much there.
All in all, I’d still rather do it than not do it.
Ahh, you’re the one I keep mistaking for JetBrains. :) Glad to have you here, J.B.
Thanks for sharing your Android perspective. It does sound similar to iOS testing. For some visual aspects like custom drawing, I may add a test after the fact, comparing against a baseline image. This lets me refactor the drawing code. But so far this is pretty unusual, because I don’t want false negatives for experimentally tweaking appearance.
For UI testing I think it is best to use a UI Automation based approach, such as Frank or Calabash.
Even then I’d keep them minimal – unless you really need a heavy handed approach (e.g. you’re working on a high-profile app with a low tolerance for regression – I’ve been involved in such a project and even the smallest UI regressions waste a lot of time as they go through test teams, get reported back, fixes prioritised etc).
I attended a presentation recently where one of the developers-in-test at Google for Selenium was saying that he felt people were using these tools *too much* and this led to brittle tests that had to be reworked a lot for perfectly valid changes and become too much of a liability.
But I still thing a small, sensible, level of UI Automation testing can act as a great smoke-test and mean you can go longer between full manual testing.
As usual the trick is in getting the balance right.
Phil, good to have you come by.
Yes, at the acceptance test level, Frank is great. (There was an announcement recently that Frank and Calabash were getting hitched.) I really like the way Frank seems to grow tests naturally.
I’m still able to TDD the bulk of my view controller code with unit tests. That may be because, while they can get complicated, they’re usually not very fancy. But yes, there is a balance in avoiding brittle tests. I tend to lean towards going full bore, until I notice any tests actually slowing down my progress.
(Folks, check out Phil’s blog post TDD – is it worth it?)
There’s a bit of a helpful distinction that could/should be drawn here between “unit testing” and “TDD”. Many developers claim that they do unit testing. Many organizations require it. In some industries (medical devices), the government mandates it! So people write unit tests – and claim that this means they’re doing TDD.
Until the bug hits the fan.
Unit testing is not equal to TDD – TDD is a methodology for writing unit tests. Why I bother with this whole “helpful distinction” discussion is that I would assert that if you are writing unit tests, but not using TDD, there’s a non-zero probability that the success of your testing is fooling you and hiding bugs.
An example… Your test calls the function under test, expecting that an exception will be thrown using this sort of construct (hopefully your testing structure has better ways of testing exceptions):
try {
classBeingTested.functionBeingTested();
} catch (Exception ex) {
Assert.assertEquals(ex.errorCode(), expectedErrorCode);
}
You run the test, it passes, and you are happy. And you are being fooled.
The test should instead be:
try {
classBeingTested.functionBeingTested();
Assert.fail("Should never get here"); // !!!
} catch (Exception ex) {
Assert.assertEquals(ex.errorCode(), expectedErrorCode);
}
Without that Assert.fail() trap, if your code NEVER throws the exception, the first test will pass every time.
This is where TDD’s rule that you must first run the test and see it fail…then write the code to make it pass. In the above example, you catch your test error before it fools you.
All that is to say, to me it’s not a question of whether TDD is worth it. It’s whether unit testing is worth it. And if it is, then TDD is a non-negotiable.
Nice example, Mike. It took me some time to shift from “I write unit tests” to doing real TDD.
Some people ask me, “Do you have unit tests for your unit tests?” In a way, that’s what the “see the test fail” step of TDD is for.
I come from a java background but principles i guess are similar everywhere.
It’s a domino effect for all the right reasons:
TDD is a medium to build rapport with your codebase.
TDD gives you flexible design as a side effect.
Flexible design gives you coverage.
Coverage gives you confidence.
Confidence helps with highly accurate reporting on estimates and budgets.
The end result being the total cost of ownership of the software you are custodian of, decreases. The cost to change one line of code in production can be measured.
I like the way you put it: “the total cost of ownership of the software decreases.” This recognizes that software is a liability, not an asset. But it’s insightful that TDD decreases this liability.