Automating testing of iOS apps with Appium and Ruby

10 July 2014 Automating testing of iOS apps with Appium and Ruby

Writing automated acceptance tests of iOS apps has always been tricky. Prior to the introduction of Apple's UIAutomation framework, there was no official way of writing automated UI-driven tests for iOS. There have been numerous third-party attempts at coming up with an automated testing solution over the years - some bad, some good - and I think I've probably tried most of them.

Testing with Frank

Until recently, my preferred choice was Frank. Its architecture meant that you could write your tests in the language of your choice (out of the box Frank was designed to be used with Cucumber) and this was very important to me - I enjoy writing Objective-C but I don't think it makes for a very elegant testing language. I'm also not convinced that trying to run your tests in an embedded pseudo test framework is a particularly good idea.

Frank isn't perfect and I'm not a big fan of Cucumber either however I have had good success in getting it running with Ruby and MiniTest (or RSpec, if you prefer) on a large client project I worked on last year. The biggest issue with Frank was the amount of effort that was required to set it up and the fact that it required a custom target and for several libraries to be statically linked into the app.

I still like Frank but there's a new kid on the block and I think it's won me over.

Testing with Appium

I was recently hired by Songkick to do some consultancy work for them, assisting them in improving their existing automated test suites. They were using KIF but they weren't really happy with their current solution. I was introduced to Appium by their head of QA, Amy Phillips as a possible solution. Given my previous experience with it, my preference was to use Frank. However, as we had set aside a few days to explore several solutions I took the time to investigate Appium.

The great thing about Appium is that it hooks directly into Instruments and Apple's UIAutomation framework so there is no need to modify your app in anyway, or create a separate target, link it to any libraries or rely on the use of any private APIs. How it works is quite simple. You can think of Appium as two distinct parts:

  • A bootstrap automation script, written in Javascript, that is loaded directly into Instruments. It opens a web socket that acts as a bridge to the Instruments Javascript runtime and provides the ability to remotely execute Javascript.
  • A server, written in NodeJS that implements the Selenium WebDriver protocol that allows you to use any existing WebDriver client to drive your tests - your app is essentially treated as a web app where the view hierarchy is your DOM and the Instruments runtime is your Javascript environment.

Unfortunately, whilst getting up and running with the Appium server was easy, my initial experience with the official Ruby client was less than great. I found its documentation to be lacking and there was a lot of emphasis on using selectors to find elements that you were interested in. I was more interested in being able to hook into the native UIAutomation Javascript API instead.

We almost gave up on Appium at that point however I had the idea of using the Ruby WebDriver client directly and seeing if we could build up our own, simple client as we went along. This approach proved to be work well - as the end goal was not to write an exhaustive suite of tests but provide my client with the tools to write their own tests, I had the time to focus on writing a really simple, well documented library. Not only did both my client and I end up very happy with the result, they were also happy for me to open source the library too.

A new Ruby library for Appium

Whilst the library started off as a very simple means of interacting with Appium, I had a goal which was to be able to use the documented UIAutomation Javascript API but in Ruby. Instead of writing this:

var target = UITarget.localTarget();
target.frontMostApp().mainWindow().tableViews[0].cells['Label'].tap()

I wanted to be able to write this:

target.front_most_app.main_window.table_views[0].cells['Label'].tap

And that is exactly what we ended up with. The library is available now for you to use but I haven't yet released an official gem. For now, you can use the latest HEAD of the project by adding it to your Gemfile:

gem 'rubium-ios', git: 'https://github.com/songkick/rubium-ios.git'

Follow the instructions on the Appium website to install Appium and the README should get you up and running with the library. I've included an interactive console example in the repository that uses Pry which is a great way to explore the API. There is also an examples repository that I will update with any examples or sample code from my blog posts.

In my next post, I'll look at using the library in more depth, including how to automate your builds and running of your tests using Rake and integrating the library with RSpec.