BDD-style testing using Objective-C

As we’ve grown our mobile practice at Pivotal we’ve tried to apply to it the same principles and disciplines that have made our Rails practice successful. Often the one that we have the most difficulty translating is testing. In my experience the testing tools for Objective-C in particular are significantly wanting; there are some out there, but they’re hard to find, often hard to use, and occasionally defective in frustrating ways.

One of the things I found I miss most in testing Objective-C, Java, or C++, is the hierarchical structure for organizing tests that frameworks like RSpec or Jasmine provide. I find nested describes indispensable for managing orthogonal aspects of the classes under test, for handling preconditions, for eliminating redundant setup code, and for generally keeping my sanity. So, when I first heard about the addition of blocks in the GCC compiler for Objective-C the first application that came to mind was testing.

So, I wrote Cedar, a BDD-style framework for writing tests in Objective-C. The code is available here. Perhaps more importantly, Cedar is in its infancy so I’m interested in any suggestions and feedback. To that end, I created a public Tracker project for it here.

Unlike OCUnit, Cedar doesn’t run magically run as part of the build. You have to create an executable target for your specs and run it. I did this because I find looking through the build output for test logging and the like to be cumbersome, among other reasons. This may or may not have been a good choice.

Yes, those are C preprocessor macros surrounding the specs. Before you get out the torches and pitchforks keep in mind that Objective-C, unlike Ruby or JavaScript, is a compiled language. This means that all imperative code must be in function or class method of some kind. In order to remove code that provided a distraction from the specs themselves I wrapped as much boilerplate as possible in these macros. When expanded, the code looks like this:

Cedar has no matchers, other than the fail() method. Rather than reinvent the wheel I decided to support using the matchers from the Hamcrest library, available here. Note that you can only get the Objective-C port of Hamcrest by checking out the code from Subversion and building it yourself. I considered committing a pre-built version of the Hamcrest framework into the Cedar repository, but I’m not sure what the accepted approach is for including dependencies like that in Objective-C projects. Feedback welcome.

All of this obviously depends upon the support for blocks provided by the GCC compiler for Objective-C. Unfortunately, this means you can only use Cedar on a Mac. Far more unfortunately, it means you have to build your specs for a runtime that supports blocks; at the moment this is only the Mac OS X 10.6 runtime. The iPhone OS runtime doesn’t support blocks (although 4.0 may), and the Mac OS X 10.5 runtime doesn’t support blocks. However, all is not lost. Plausible Labs provides patched versions of the GCC compiler and runtimes for iPhone OS and Mac OS X 10.5. I built much of Cedar on a Leopard machine with the PLBlocks compiler; I haven’t tried building for iPhone OS yet, I look forward to hearing about any experiences.

Don’t try to mix blocks with Objective-C++, at least not yet. I tried it for some time and ran into any number of internal compiler errors. Hopefully this will improve in the future. As some will astutely point out, I could have used the anonymous functions introduced by C++0x (and supported by GCC). Unfortunately (from Wikipedia):

If a closure object containing references to local variables is invoked after the innermost block scope of its creation, the behaviour is undefined.

There’s no need to provide a header file for your specs, since Cedar finds the specs by introspection.

As I’m sure will soon become entirely obvious this is very much a minimal viable product for Cedar. You can create and nest describe blocks, create examples and beforeEach blocks, and that’s about it. I’m curious to see if people will use something like this; if they do, I’m hoping for plenty of feedback. I’m attached to basically nothing about the framework at the moment (including the name), so please send me a note or join the Tracker project if there’s something you’d like to see added, removed, or changed.