Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
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.

A minimal spec in Cedar looks like this:

// FooSpec.m

#include "SpecHelper.h"
SPEC_BEGIN(FooSpec)

describe(@"An example", ^{
  it(@"should be descriptive and helpful", ^{
    ...
  });
});

SPEC_END

A few things to note:

  • 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:
// FooSpec.m

#include "SpecHelper.h"

@interface FooSpec : Spec
@end

@implementation FooSpec

- (void)declareBehaviors {
  describe(@"An example", ^{
    it(@"should be descriptive and helpful", ^{
      ...
    });
  });
}
  • 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.

Comments
  1. Wow, this looks really nice! As a (primarily) Ruby developer, I definitely miss RSpec or Shoulda’s nested contexts when programming in Objective-C. I just wish I could use this for iPhone development. Hopefully iPhone OS 4.0 will support blocks…

  2. Jakub Suder says:

    Man, you rock! Though I probably won’t be able to use this very soon, since it doesn’t run on the iPhone because of blocks (I really hope they add them in 4.0)… But I love the syntax – anything that limits ObjC’s crazy long-windedness and makes it a bit more DRY is a step in the right direction, and macros are a great tool for that; I also use them whenever I can, since that’s the closest thing to metaprogramming available in ObjC.

    I may finally start writing tests for my ObjC apps once I’m able to use your library on the iPhone :>

  3. Max Hawkins says:

    Nice work!

    I was just about to write something like this. Thankfully I found your page before I did. This will be a big help.

  4. Adam Milligan says:

    For those who think you can’t use this now to test iPhone projects, think again! I’ve written more about the iPhone specifically [here](http://pivotallabs.com/users/amilligan/blog/articles/1267-bdd-style-testing-for-iphone-projects).

  5. Hi Adam. It took me a few minutes to get up and running as I retraced the Usage notes a few times. One bump was creating the main.m file for the Spec target. This might be obvious to some, but I needed to create a separate folder via the Finder in my project directory, which I called Spec for convenience, and added a file called main.m. This was to avoid overwriting the project’s pre-existing main.m. Is this what you’re doing wrt suggested “best practice”? Thanks and looking forward to digging deeper.

  6. Adam Milligan says:

    Alessandro, I generally set up the project on the file system with two top level directories, App and Spec. I also generally have parallel subdirectories under those for different things, but you can set that up however you like. In any case, this is my long winded way of saying yes, you set it up exactly as I would: my main.m file for specs goes into the Spec folder.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *