Real Life iOS project using TDD Techniques

iOS + TDD = Winning Combination

I just finished a short-term iOS project where I used TDD (Test Driven Development) techniques to good advantage. I know I ended up with a cleaner design and better code than I would have otherwise.

I am not a TDD expert, nor am I a TDD purist, but I wanted to share my experience and lessons learned while it was fresh. Hopefully this will encourage other developers to add more testing to their development diet. Also, while this is not a how-to post, I did include a few links at the end of this post.1

The Project

My task was to develop a simple template based, dynamic data entry module to be used in a larger project with a very near-term delivery date. The overall client requirement for the application was to download entry form templates from a server to an iPad, where their customer would fill in a form similar to existing paper forms. Once completed, the customer could submit the results.

The high level requirements for my module were:

  • Parse a simple text-based template file,
  • Produce a dynamic form with static text and labeled text fields,
  • Properly layout the form during rotation or resizing events,
  • Provide simple methods to manage the field values on the form.

Roughly, my time looked like this:

  • 10% — Up-front design and discussions with other developers
  • 20% — Testing and development of the parser
  • 15% — Testing and development of the form builder
  • 30% — Testing and development of the layout engine
  • 15% — Building a demo app to show how the module works on a device
  • 10% — Testing and implementing modifications

Because of the tight deadline, we designed a simple format for the template that allowed for headings, subheadings, and paragraphs of text with embedded text fields of varying lengths. Here is how things turned out.

Sample Template Text

#Customer Visit Report#

##Arrival##

The customer, [[CUSTOMER_NAME|10|Customer name]], entered [[STORE_NAME|10|Store]] 
at [[ARRIVAL_TIME|5|Time]] on [[ARRIVAL_DATE|5|Date]]
to [[VISIT_PURPOSE|10|Purpose]].

##Departure##

The customer left through the [[EXIT_LOCATION|5|Exit]]
at approximately [[DEPARTURE_TIME|5|Time]].

Demo App Editor

RealLifeTDD 1

Demo App Form

RealLifeTDD 6

The field values for the template can be set before or during an edit session, and it can be told to generate a dictionary of field names and values at any point in the process as well.

The Process

At it’s core, the TDD process is:

  1. Decide what behavior to implement next,
  2. Write a failing unit test that exercises that behavior,
  3. Write the smallest amount of code it takes to make the test pass,
  4. Refactor the system as needed,
  5. Repeat.

At each step during this process, the entire suite of tests is run often to make sure no existing behavior is inadvertently broken.

My simplistic understanding of pure TDD was you should write unit tests even before the class or method that will implement the behavior exists. However, my development toolset of Xcode, GHUnit, and OCMock made that scenario pretty awkward, which slowed me down. I needed something with a better flow, so in my process, I:

  1. Decided what behavior to implement next,
  2. Added a appropriate unit test by:
    • Writing a failing unit test that exercised that behavior,
    • When necessary, creating a minimal class or adding an empty method so the target under test would build,
    • Ensuring the test target based on GHUnit/OCMock would build,
    • Ensuring the tests ran to completion in the simulator,
    • Ensuring the older tests still passed,
    • Ensuring the new test failed.

    If any of that did not happen, I made changes to the tests or module until it did.

  3. Wrote the smallest amount of code it took to make the new test pass,
  4. Refactored the system as needed by:
    • Extracting duplicate code in the tests or module into common methods,
    • Removing extraneous parameters or methods,
    • Simplifying the design as new insight is gained,

    During which I ran the entire test suite often to make sure nothing was inadvertently broken.

  5. Occasionally ran the demo app to check the visual behavior,
  6. Repeated until finished.

I am now accustomed to this style in Xcode, so each iteration went very quickly.

Limitations of Current Toolset

I did not find any limitations that would make me second-guess my decision to build this module using TDD, but there were some minor hassles.

I really like OCMock’s implementation of mock objects for Objective-C, but I had to make small changes to some module code to be able to use it. For instance, in one set of methods I would have preferred to use a primitive variable for the argument, but I needed to use a NSNumber instead because of the nature of the test I wanted to write. Even so, there were many more cases where primitive method arguments worked just fine in my tests, so it really was just a minor annoyance.

It is pretty straightforward to run a single test in GHUnit, but once I started adding more and more tests, it would have been nice to have a quicker way to do that. This project was fairly small, but I can envision some issues with a larger project. My core library ended up with 9 class files, and the test target had 9 class files with 47 unique tests. I definitely would want better ways to run individual tests and subsets of tests as the number of tests grows. At some point in the future, I would like to modify GHUnit to make this easier.

GHUnit does a decent job of sorting out the log output for each test so you can find the root cause of failures, but I find the standard Xcode/NSLog output too cluttered. It is important to form the habit of running tests very frequently, so anything that speeds up each loop is a good thing. A combination of easier control of which tests I want to run and finer control of the log output would smooth out the whole process.

While not a limitation of TDD or testing tools in particular, I wish the refactoring in Xcode 4 was better. I am convinced that if Xcode had better automated support for common refactorings such as extract method it could have reduced my development time by 5-10%. I know some Objective-C developers may not like Java, but the refactoring support in JetBrain’s IntelliJ is world-class. I have high hopes for their AppCode tool.2 Perhaps it will be popular enough to have an influence on future Xcode versions.

Lessons Learned

I really enjoyed using these techniques and I plan on following this pattern as much as possible in future iOS projects.

What I learned:

  • Testing non interface elements was a no-brainer, doing that is the minimum I should do for every project.
  • Testing the methods that arranged view layouts worked better than I expected.
  • GHUnit fits my development and testing style better than OCUnit.
  • Using OCMock to manage mock objects made writing tests easier, so it greatly increased the number of tests I was able and willing to write.
  • I want to modify GHUnit to support better organization and management of individual tests.
  • I need to spend more time with OCMock to discover if my workaround was really necessary.
  • I need to investigate Xcode logging techniques.
  • I need to give the latest version of JetBrains AppCode a serious try.

If you are not already doing so, I highly recommend adding automated testing to your development process. I also encourage you to investigate TDD to see if some of it’s techniques can help you build better code more reliably.

Have a great day!


This is post 2 of 10 on my second iDevBlogADay run.

We all need the support of others to do our best work. Find other like-minded developers that will provide encouragement and motivation through local user groups, conferences or meetups. A great collection of indie iOS developers have helped me stay on track through meetups, 360iDev, twitter, and iDevBlogADay.

I regularly attend Cocoa/iPhone developer meetups in Cincinnati, Ohio and Columbus, Ohio. If you are in the central or southwest Ohio area, come join me at either monthly meetup:

If you depend on iOS development for your livelihood, or would like to get to that point — you really need to attend a conference dedicated to helping you get better, and I can think of no better conference for that purpose than 360iDev — you should register today!. Much of what I am able to do professionally is due to the things I learned and the people I met there.

Finally, here is a little more information about me, Doug Sjoquist, and how I came to my current place in life. You should follow me on twitter and subscribe to my blog. Have a great day!



1. GHUnit, Xcode, and TDD links

  • I wrote a blog post on setting up Xcode 3 to use GHUnit and OCMock,
  • I am working on a series of tutorials on TDD and iOS using Xcode 4 for Ray Wenderlich’s tutorial site, http://www.raywenderlich.com, the first of which should be available very soon,
  • I am making a presentation on OCMock at 360iDev in Denver in September.

2. JetBrains AppCode — an alternative to Xcode.