Unit Testing iPhone apps with Ruby: rbiphonetest

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.
Using Ruby within TextMate snippets and commands
I didn’t know you could run Ruby within TextMate snippets. As a consequence, a lot of the TextMate bundles I work on either have simplistic Snippets or the advanced code is run via Commands with code pushed into explicit Ruby files in the Support folder.
But sometimes I just want a clever snippet. For example, I want the ‘cla’ snippet to use the current filename to create the default class name instead of the current ‘ClassName’ default. I want default foreign key names to be meaningful.
I’ve now figured this out (thanks to Ciaran Walsh), and …
Um, lost already? Ok, let me show you via screencast on Snippets and Commands with Ruby (QuickTime (11Mb)):
TextMate Snippets running Ruby from Dr Nic on Vimeo.
Want to learn more about living with TextMate and Ruby?
The TextMate website has a series of videos, including one by the Ruby.tmbundle’s own James Edward Gray II (JEG2).
In addition, there is the latest TextMate for Rails 2 Peepcode written by myself and spoken by Geoffrey Grosenbach. Its cheap at $9, good value at $15.50, and perhaps overpriced in the $20-$30 range. Lucky its only $9.
The snippets used throughout the video
The current Ruby.tmbundle snippet (activated via ‘cla’):
class ${1:ClassName}
$0
end
An attempt to use regular expressions to convert the filename to a classname:
class ${1:${TM_FILENAME/[[:alpha:]]+|(_)/(?1::\u$0)/g}}
$0
end
The final snippet, with embedded Ruby to do the heavy lifting (note: added ’singluarize’ to the snippet):
class ${1:`#!/usr/bin/env ruby
require 'rubygems'
require "active_support"
puts ENV['TM_FILENAME'].gsub(/\.rb$/, ”).camelize.singularize
`}
$0
end
Add this to your own Ruby.tmbundle, or clone mine (which is a clone of the original subversion repo).
How to yell at people with GitHub from TextMate
Sometimes when you are perusing code you ask the question: why the hell is that there? or why does this even work?
Now you can instantly navigate from that erroneous line to the git commit where it was added, and then using github’s commenting system add a full-flavoured remark about that person’s code. I’m not sure if profanity is against the GitHub Terms of Service, but I’d rather ask forgiveness than permission.
TextMate + GitHub - how to comment/discuss on a line via GitHub from Dr Nic on Vimeo.
Download and installation instructions are available in all good bookstores.
Just for TextMate?
There is an editor for Windows - E-TextEditor - that was designed to support TextMate bundles. So far, the GitHub bundle doesn’t use any special features of TextMate’s latest-and-greatest UI libraries, so it should be usable on E-TextEditor.
Also, a VIM project has been created to port the GitHub bundle, by Christoph Blank. Cristoph can be found hanging around #hobo on irc as ’solars’, if you want more goodies in the VIM bundle.
GitHub and TextMate Unite
I wanted to go from a source file to the equivalent file on github. I wanted a selection of lines in TextMate editor to also be selected when I was taken to github.com. I wanted to cut back on my senseless killing of innocent pasties.
Finally, I wanted to make a nice little video to show off the new GitHub.tmbundle.
TextMate and GitHub: Show the current file in GitHub from Dr Nic on Vimeo.
Which remote repository is it choosing?
If you have multiple remote references to github.com repositories, then the algorithm picks one in the following order:
- A remote named ‘github’
- A remote named ‘origin’
- The first remote for a github.com repository
What else could go in a GitHub textmate bundle?
Rails 2.0 TextMate bundle - Tasty Tidbit - respond_to and view navigation
The new release of the Rails TextMate bundle is coming soon. Its guaranteed to be shiny, sparkly and will fit in with any home or office decor. More importantly, it will be upgraded for Rails 2.0.
Today is the first Tasty Tidbit - a demonstration of one of the snazzy new features coming to your Macintosh soon.
In this Tasty Tidbit, we look at respond_to and the ability to create and navigate to view templates based on the selected format block, such as wants.js -> .js.rjs.
Cannot see the embedded video? Want the Hi-Def version? Download the video (5 Mb).
Contribute to the Bundle
To clone the git repository and start sharing your own personal goodness, see previous article.
