Dr Nic

Unit Testing iPhone apps with Ruby: rbiphonetest

rbiphonetest logo

Everything to love about Ruby: the concise, powerful language; the sexy testing frameworks; and finally, the people.

Everything to love about Objective-C: hmmm; well…; and finally, its the only high-level language you can use to write iPhone apps.

On iPhone 2.0, to arrive on the 11th of July, you cannot run RubyCocoa. But you can run it on your Mac, so let’s use it to unit test your Objective-C classes. This tutorial shows you how to get started using a new project rbiphonetest [GitHub | Lighthouse | Google Group]

If you followed some of my recent tweets, this project was previously called “iphoneruby”. And alas, the screencast also calls it “iphoneruby” but that was a crap name. People thought it was a way to run Ruby on the iphone. I can’t do that yet. So, a far better name is ‘rbiphonetest’. [track on summize]

Even if you’ve never touched Objective-C, Cocoa, the iPhone SDK, nor RubyCocoa I recommend watching the video anyway. It should give you hope that if you make the transition to iPhone development you don’t have to go alone without Ruby: your trusty swiss army knife of language/libraries/tools.

The screencast is also available in high-def video (55Mb QuickTime)


Unit Testing iPhone apps using Ruby from Dr Nic on Vimeo.

Installation and Usage

To summarize the video, but change ‘iphoneruby’ to ‘rbiphonetest’, you install the framework via RubyGems:

sudo gem install rbiphonetest

Then change to your project’s folder and install the test framework:

rbiphonetest .

Finally, for each generic, non-UIKit-framework-using class you want to test:

script/generate model WidgetModel

Then write your tests in test/test_widget_model.rb

Supported Cocoa & iPhone frameworks

The mysterious, magical premise upon which rbiphonetest depends is possibly erroneous: that your Objective-C class can be compiled and tested against your OS X/Intel frameworks, and if your tests pass you assume you can then compile and include your class with the the iPhone/ARM frameworks.

I’m willing to go with this assumption until its proven dangerously flawed by some angry 20-year veteran of NextStep/Cocoa/iPhone. But really, how different could NSString be on the iPhone versus your Mac?

Fortunately there is one way to check for significant differences between your available Mac-based frameworks, such as Cocoa, and the iPhone-based frameworks, such as UIKit. We need to compare the framework names, header files and method signatures.

So for example, you cannot currently unit test any class that depends on/includes the UIKit framework. Why? It doesn’t exist on your Mac, so the Mac/Intel compiler cannot link it in. We’re compiling and running our tests with RubyCocoa, which itself is built against the Mac/Intel frameworks, not the iPhone frameworks. Hell, Laurent doesn’t even own an iPhone :) [Laurent is the Apple-employee maintainer of RubyCocoa and the newer MacRuby]

Similarly, its no use including/linking the Cocoa framework into your Objective-C class. Why? It doesn’t exist on the iPhone. It has its own UI frameworks, collectively called ‘UIKit’.

So for the moment we cannot test UI-related, iPhone-API-specific code. But we can test generic Objective-C. That’s better than a kick in the teeth. Surely. I mean, in the teeth… that’d friggin’ hurt.

“Fair enough Dr Nic, so which frameworks can my code use and yet still unit test it with your oh-so-special test library thingy?” Keep your pants on, I’m getting there. [ref]

To the best of my ability, I’ve compared the two sets of frameworks and listed the available Frameworks that are available on both the iPhone and your Mac. There are about a dozen. The most important is called ‘Foundation’. It holds gold nuggets like ‘NSString’.

The list of platform differences is on the wiki as a reference.

Note, this list doesn’t guarantee that any two framework classes – the iPhone and matching Mac framework – will behave the same. This list is compiled by finding the set of Frameworks with the same name on both platforms, e.g. Foundation.

Then it compares the set of public header files (Foundation.framework/Headers/*.h files) This comparison is by method signature. It pulls all lines from each header that start with + or – (+ is a class method and – is an instance method in Objective-C) and compares the two lists. If there is a single difference in the method signatures of the header files in the two platforms it is marked on the wiki page. You’ll need to look at the two header files yourself to see the differences. Some header files are ugly. C-based anything starts ugly and goes down from there, I think.

Python testing of iPhone Objective-C?

In the Python world there is PyObjC, a bridge-based twin to RubyCocoa. If you are a Python developer you could easily port this project to use PyObjC I would think. Ping me if you are attempting this and need any help.

Summary

I think this project can give Ruby developers a happy place to work from as they write their Objective-C/iPhone code. You still need to wire up your UI views and controller classes manually, but if you push all the “oooh that code really needs some tests” classes away from the UI-dependent frameworks then you can hook it up to rbiphonetest and write your tests in Ruby.

Currently the generator creates test/unit test stubs. I personally then add the Shoulda gem into my test_helper.rb for my apps. If an rspec and/or test/spec developer can help with adding support to the generators I’m certain the large rspec user-base would be happy campers.

Similarly, someone might like to investigate using MacRuby to run the tests instead of RubyCocoa. Fast tests vs slow tests. You choose.

What the?

Sometimes I re-read what I’ve written and notice things that don’t seem to make sense, but are in my vocabulary nonetheless. Yep, the things you learn living in Australia.

“Keep your pants on” – this seems to imply that until I mentioned otherwise you were about to take your pants off. Hardly relevant at any stage during this article, we’d both agree. Most code-based blog articles are “pants on”. This phrase means “don’t get upset”. You can try to figure out how you go from “don’t get upset” to “keep your pants on”. I have no idea.

Related posts:

  1. Validate and Save your Ruby in TextMate – with secret Rubinus superpowers In some TextMate bundles, if you save a file it...
  2. Closing in on The Dream: “one-click-to-deploy Rails apps” Got a simple app you want to build? Allocate...
  3. Future proofing your Ruby code. Ruby 1.9.1 is coming. Bugger. I’m a Ruby monogamist. I use the Ruby...
  4. To WebKit or not to WebKit within your iPhone app? I know HTML. Its on my CV. Expert level....
  5. iPhone dev podcast about fmdb-migration-manager and rbiphonetest Radio is where ugly people go. Podcasts is where ugly,...

35 Responses to “Unit Testing iPhone apps with Ruby: rbiphonetest”

  1. Chris says:

    BTW Nic, you can easily use C/C++ on the iphone just like you would on a normal mac. The only thing you actually need to use ObjC for is to talk to the Cocoa APIs.
    It very straight forward to turn on ObjC++ just change a .m file to .mm and you can mix and match from all 3 languages at will for what ever is best for the current job.

  2. Nice! =)
    But I do think it is easier to get upset with pants on than off!

    (Sorry, but I had to drop this comment! =P)

  3. Hi Dr Nic, Thanks for starting this project! I’ve been wanting to do ruby testing of my cocoa apps for a while, but I never figured out how to compile bundles and load them into RubyCocoa.

    I was playing around with this last night, and I got some rspec specs and stories running with your framework. I’ve posted the code here: http://github.com/avh4/rbiphonetest-example/commits/rspec

    I’d be glad to help however I can to add add rspec support to rbiphonetest.

  4. Dr Nic says:

    @aaron – awesome; we’ll talk about it on the mailing list. Ultimately I need to fragment out the test/unit stuff into specific generators and use rubigen dependencies. Fortunately merb + newgem have done this in the past so we can steal those ideas.

  5. Dr Nic says:

    Apple has a page describing differences between OS X and iPhone frameworks: iPhoneOS for Cocoa Developers

  6. [...] Unit Testing iPhone apps with Ruby: rbiphonetest – Does Dr. Nic ever sleep? [...]

  7. Jon Hohle says:

    Since you cannot test iPhone specific libraries anyway, why not use the built in otest for writing unit tests?

  8. Dr Nic says:

    @Jon – ultimately its a starting point for pushing RubyCocoa closer to the UIKit frameworks; perhaps onto the iPhone itself one day. I find Ruby cleaner and more concise for writing code that also communicates what it is doing; I also love the Ruby-based test frameworks such as rspec and test/unit, and the surrounding tools (such as autotest).

    I figured it would be beneficial to release the tool now, even without full UIKit coverage.

    But, people already familiar + happy with one of the current Objective-C test suites would probably keep using those. I’ll probably investigate these test suites soon to see how easy they make UI testing etc.

    Again, I prefer writing/reading Ruby, so I want to get as much Ruby into my development code, tests and support tools as possible. It makes me happy and productive.

  9. Great stuff Nic! There is a tool that’s used in the Rakefiles generated by RubyCocoa which allows you to programatically add files to an XCode project. You could likely use this instead of manually adding the files:
    https://rubycocoa.svn.sourceforge.net/svnroot/rubycocoa/trunk/src/framework/tool/rubycocoa/

  10. Grammar Police says:

    You may “infer” something when you hear a message; you can “imply” something when you send a message (and you might judge the implication to exist in the message independent of the sender’s intent to put it there). Your last paragraph should start:
    *“Keep your pants on”* – this seems to *imply* that until I mentioned otherwise you were about to take your pants off.

  11. Dr Nic says:

    @Justin, ahh thanks for pointing those helper libraries out. When I did the video originally and I got to the final linking error I actually didn’t know what I’d done wrong. And linking errors are annoying. The section where I fix the linking error was cut into the video later when I figured it out. I definitely want the generated files to be auto-added to the xcode project, so I’ll reuse the RC tools code; thx for pointing out where it is.

  12. Dr Nic says:

    @GP, I’ll take any grammar advice on offer. Thanks.

  13. [...] lately and, unsurprisingly, has found a way to bring Ruby into the mix. He has developed rbiphonetest, a Ruby-based testing framework for iPhone / Objective C applications that uses RubyCocoa to [...]

  14. Peter Jaros says:

    Awesome! I was preparing to do the exact same thing after WWDC, but I ran into some snags. I’m glad I didn’t duplicate your efforts!

    Quick question, though: what does this have to do with the iPhone in particular? Aren’t you just unit testing Obj-C classes in Ruby? I mean, it’s great that it *does* work for iPhone development, but this would be great for desktop Cocoa development too.

  15. Dr Nic says:

    @peter – it can definitely be used for mac cocoa development too (I think mention it a couple times to cover my bases). I didn’t figure too many people would use it for that, since with Cocoa development you have the option to write the whole app in RubyCocoa or in the future, MacRuby.

    Nonetheless, Aaron VonderHaar has used it for some projects (http://groups.google.com/group/rbiphonetest/msg/81a21d5d304fb239) and has some thoughts on mocking NSObject subclasses.

    If lots of people use it for Cocoa dev then we can talk about changing the project name :)

  16. [...] Unit Testing iPhone apps with Ruby: rbiphonetest [...]

  17. [...] hmmm well?? and finally, its the only high-level language you can use to write iphone apps.http://drnicwilliams.com/2008/07/04/unit-testing-iphone-apps-with-ruby-rbiphonetest/macosxhints.com – Watch slow motion video on the iPhone and iPod touch… either on YouTube or your [...]

  18. steve longdo says:

    You can download the SDK (free signup to Apple iPhone Development program: http://developer.apple.com/iphone/program/ ) and compile against the UIKit and CocoaTouch libs. It would be awesome to get Ruby code to simulate gestures, etc.

  19. Thomas says:

    thats pretty cool, thanks for the hint

  20. Matt says:

    Awesome, I look forward to trying it out today at the dev camp.

    (PS: you may want to add this dependency to the gemspec: rubigen) :)

  21. stefan says:

    Hi,

    I really like your testframework!

    Just a quick note:

    It should be [calculator relase]; instead of [calculator dealloc]; in
    - (void) dealloc;

    Cheers,
    Stefan

  22. Rusty Zarse says:

    Thanks for the gem, Dr Nic!
    I am new to mac development, coming (screaming) from a Windows world. I have been slowly dabbling in Ruby and am quite pleased for this business driving opportunity to finally pursue it seriously.

    Finally, I believe “keep your pants on” is derived from “don’t get excited” rather than “don’t get upset” which makes more sense should you imply that before you said topic phrase, we were all so excited we were about to take our pants off. I’ll let you ‘infer’ for what purpose….

  23. [...] God for people like Dr Nic. I managed to test drive a simple calculator project on the iPhone using his screencast. Just a quick note if you don’t come from the Ruby camp, (you know, like you spend most of [...]

  24. Simon T says:

    Nice one Dr Nic, I have been wondering how to get my iPhone app done test first and finally stumbled across your blog…SUBSCRIBED!

  25. Cliff says:

    RSpec? Has RSpec been successfully run on your Ruby iPhone testing framework? I’ve seen bits and pieces of promise from the project page to the email lists but I’d like to see a screencast or some official documentation. I could figure it out eventually, I’m sure but since I’m new to both ObjC and Ruby I’d liet ot hear from the pros. I’m at a point in a project where I could definitely use something like RSpec to describe the screen flow. Tell me there’s a simple solution here, please?

  26. Dr Nic says:

    There shouldn’t be any issues with using rspec vs test/unit. You just need the line that loads the bundle into RubyCocoa (OSX.ns_import :Calculator)

  27. [...] BowlingController as a vanilla NSObject using the “script/generate model” command from Dr. Nic’s RBiPhoneTest. I use rbiPhoneTest to setup the project test structure as well so now I have a rake file in my [...]

  28. [...] Sun 07-12-2008 Fix top level domain list in Mobile Safari Saved by thecdude on Sun 30-11-2008 Unit Testing iPhone apps with Ruby: rbiphonetest Saved by rytnelle on Tue 18-11-2008 Update: Einstein emulator on the iPhone Saved by nextManage [...]

  29. Rob says:

    I don’t know if this is useful, but looking at some iPhone example code, SQLiteBookList, I see this in the book.h model class:

    #import

    Book isn’t an iPhone specific class, so I would hope that the Jobians would only ship example code that will run both on a mac and an iPhone.

    This test framework looks totally rad. I am very very very new to iPhone / mac development and have been looking for a way to do test first. Thanks!!!

  30. Rob says:

    ummmm…..

    “#import” in my previous post should be:

    #import <Foundation/Foundation.h>

    forgot the escapes.

  31. Rob says:

    One more question: My model classes are going to be tied to tables in an SQLite database, is there a slick way to use fixtures to populate the db with test data? Kind of like how it works with rails?

  32. [...] Recent public urls tagged “objectivec” → Unit Testing iPhone apps with Ruby: rbiphonetest [...]

  33. rand says:

    stupid question, but how do you get XCode to autocomplete your alloc / init etc complete with square brackets and semicolon?

  34. [...] rbiphonetest seems like a great project and I imagine several of the “pure” Web developers on my [...]

  35. Jon Rose says:

    You might also want to checkout Gorilla Logic’s FoneMonkey, an open source record / playback testing tool for iPhone applications: http://www.gorillalogic.com/fonemonkey