May 2011

Real Life iOS project using TDD Techniques

Filed in iDevBlogADay, iOS Development | Comments (0) |

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.

Are You Being Energized or Drained?

Filed in iDevBlogADay, Miscellaneous | Comments (0) |

Things That Energize Me

Eric

EricYesterday I was in the basement talking with one of my sons. I heard laughter upstairs and went to see what was up — at the top of the stairs I found my grandson quickly crawling towards me. He had heard my voice and decided being with grandpa sounded like a pretty cool thing, and took off like a shot to come find me.

How great is that!

Often when he is around, a short work break turns into an extended session crawling around on the floor until my knees hurt. When that happens, I always return to my basement lair refreshed.

Making Physical Things

It has been a little while since I have done any significant woodworking, but I do have a decent set of tools and supplies in the garage. I love starting with nothing more than a vague idea and transforming raw pieces of wood into something with my own two hands (and some power tools!).

Creating physical things that never existed before is very cool, and seeing the finished product sitting on the workbench is satisfying.

Wilderness

GlacierNPI love spending time with my family, and some of my favorite times are our trips to the mountains, woods, or canyons. Being someplace where none of my technology works frees me in a unique way. Even a short visit to the wilderness can recharge a part of my mind and soul that I didn’t even realize were hurting.

A hike in the canyons of the southwest, the great woods of the north, or one of our great National Parks in the mountains inspires me in a way nothing else can.

Helping People

When someone I have taught demonstrates by their work or life that they “got it”, that gives me a good feeling. But, when they surpass anything I taught or helped with — that is really exciting.

When someone like that excels, their success spills over and puts gas in my tank.

Writing Useful Software

As much as I like seeing something physical take shape, crafting something out of nothing but thought is a uniquely enjoyable experience. When the pieces come together and fit just like I want; when I can look at what I have written and honestly say to myself, that turned out pretty well; and when others find what I have finished to be good — I am pleased, and that pleasure provides energy for the next round.

Things That Drain Me

Breaking Commitments

One of the requirements of being a responsible adult is making commitments and keeping them. When I can see no way to keep a commitment I made because of my poor planning or lack of understanding of what I was doing — I pile up stress points like crazy. If I have to go back to someone and explain that I can see no way to keep the commitment I willingly made earlier, it is a serious emotional drain and can turn into a downward spiral if I am not careful.

Making a Mistake That Hurts Someone Else

Mistakes happen. We all know that. If I mess up on something that primarily hurts me, I kick myself for a minute then get my head on straight and figure out how to correct it and move on. I really don’t waste energy on the “if only” stuff any more. But, if my mistake causes someone else extra work or pain, I cringe and struggle with figuring out how to make amends.

Repeatedly Failing To Reach A Goal

If I set a goal, whether formal or informal, and don’t reach it — I am usually able to rationally analyze what went wrong and decide how to proceed. But, if there is a long term goal that I seem to be getting no closer to, or repeat the “set goal, fail” cycle for the same goal too many times, it gets very discouraging.

Beating Discouragement

We all could make lists like these. Some of the items will change, but the pattern is part of being human. One of the dangers of discouragement is that it starts a negative feedback loop that can ruin me emotionally if left unchecked.

To be discouraged is “to be deprived of courage or confidence : be disheartened”. When my courage or confidence is low or non-existent, it can be very hard to break out of that cycle. Here are some things I can do to break discouragement’s nasty grip.

Separate The Objective Reality From Subjective Feelings

Emotions are a vital part of being human, and it does not help if I pretend they don’t exist or that they don’t matter. Unfortunately, emotions can be misleading or just plain wrong, and often lag far behind objective reality. I need to find a way to sort out what is real.

I need to work backwards from my feelings to the reality. I do something like the “Five Whys” on my emotions to help determine the root causes. Once I have identified the root causes of my discouragement I am in a better position to address them.

For root causes that are outside my control, I have to ask myself if I can really afford to let events like that control my life so deeply. At this point, I need to force myself to change my goals or expectations. If I am unwilling to make that change, I need to realize I am facing a downward spiral with no escape.

For root causes over which I have some control, I have to ask myself which is more important: the status quo those causes represent or my goal. It may be difficult to decide, and my end decision may be a hybrid, but once again, something needs to change or nothing will be solved.

Enlist The Help Of Objective Loved Ones And Friends

If I am having trouble sorting out the truth about my discouragement, enlisting an objective loved one or friend makes a big difference. Even the geekiest, most introverted people need others, and I am no different. It can be painful to ask for this kind of help, and I need to use discretion, but it may be the only way for me to see what is true and what is false.

Choose To Do Things That Energize You

I need to find those things that energize me. I cannot expect to remove destructive emotions without finding things to take their place. When I have a choice, I need to choose activities, relationships, and projects that will be encourage me to keep trying.

I need to find ways to take incremental steps towards my goals. When I see progress on a small scale, that enables me to envision success on a grander scale.

Be Decisive

It is a myth that I can have it all. I need to make choices about what is important to me, what I want, what I should do, and what I cannot do.

One aspect of leadership that applies to all responsible adults is that decisions must be made with incomplete information. I will never have all the information I want before I have to make a decision, and if I keep waiting for it, I will end up in “analysis paralysis”.

So.

  • Make a decision.
  • Be willing to live with the consequences of that decision.
  • When necessary, make changes.

After all, that is what it means to be a grown up.


This marks the beginning of my second run on iDevBlogADay. Once again, my thanks go out to all the participants, readers, but especially to Miguel. Thanks Miguel!

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!