Four levels of customization in Calabash-Android

The most important benefit of Calabash is that you can write your test in business-readable domain-specific language, which means you should take the pre defined steps and write custom steps. I will show how to really create custom steps here.

There are four levels, level one and level two and already covered by a famous blog and the Calabash-iOS documentation, but I will go through them again to make it complete.

Note: we will not assert anything here, the purpose is focused on automation.

Before we start hit the Calabash-Android’s Githut home to make sure you properly installed it. Then create a folder for your test, then cd this folder and type “calabash-android gen”. Then grab the Skype Android app as our test app. You can get it from Google Play, then you should re-sign it into debug mode so you test can access the app. Put the apk in the test folder you created.

Pre Level: no customization.
We will start by writing a test that uses all pre-defined steps. In your test folder, open my_first.feature and type in following code (change my_username and my_password” to your username and password:

Feature: Voice Call
Scenario: As a valid user I can log in to Skype and make a voice call

Given I wait for “Sign in” to appear
Then I enter “my_username” into input field number 1
And I enter “my_password” into input field number 2
And I press the “Sign in” button
And I press the “Enable” button
And I wait for “Contacts” to appear

When I press “Contacts”
And I wait for “Echo” to appear
And I touch the “Echo” text
And I wait for “Voice call” to appear
And I press “Voice call”
And I wait for “00:05” to appear
And I take a screenshot
And I press image button number 3
And I go back
And I go back
And I press “Sign out”
And I press “Yes”

Now go to your test folder and run

calabash-android run com.skype.raider.debug.apk

or what ever the name of your apk is. It is fun, isn’t it?

Level 1: Macro
You notice the pre level test is just like old style procedural program. For a block of code that does one thing, we can just use macro to group it into a new custom step. Here is how:

Go to the step_definitions folder in your test folder and open calabash_steps.rb, and add the following code to the end.

Given /^I am logged in as a tester$/ do
macro 'I wait for "Sign in" to appear'
macro 'I enter "my_username" into input field number 1' #replace my_username with your real username
macro 'I enter "my_password" into input field number 2' #replace with real password.
macro 'I press the "Sign in" button'
macro 'I press the "Enable" button'
macro 'I wait for "Contacts" to appear'
end

now you can go back to your my_first.feature and replace the first block with just one line.

Feature: Voice Call
Scenario: As a valid user I can log in to Skype and make a voice call

Given I am logged in as a tester

When I press “Contacts”
And I wait for “Echo” to appear
And I touch the “Echo” text
And I wait for “Voice call” to appear
And I press “Voice call”
And I wait for “00:05” to appear
And I take a screenshot
And I press image button number 3
And I go back
And I go back
And I press “Sign out”
And I press “Yes”

Now run it. your code is much shorter due to this customization! You should do this to all blocks of your test scripts that you use quite often. This is just like create a function and then later on you just call the function instead of writing all the code again. Now level two!

Level 2: add Ruby/Cucumber code in calabash_steps.rb file in the test folder

This requires you to understand what the pre-define the steps really, so dig into the source code of the step definition at

https://github.com/calabash/calabash-android/tree/master/ruby-gem/lib/calabash-android/steps

to read them. For our tutorial, we want to add a custom step to make sure we are on the Skype home screen (not really necessary, but doing it for the purpose of the tutorial).
Notice that the “Share” EditText filed always say “Tell friends what you’re up to”, so we know when when see “Tell friends”, we are on the home screen. This tells us we can modify pre defined assert steps to do our job. Open
https://github.com/calabash/calabash-android/blob/master/ruby-gem/lib/calabash-android/steps/assert_steps.rb
and copy

Then /^I see the text "([^"]*)"$/ do |text|
performAction('assert_text',text, true)
end

to the end of you calabash_steps.rb and change it to

Given /^I am on the Skype Home Screen$/ do
performAction('assert_text', 'Tell friends', true)
end

and add one line to your my_first.feature

Feature: Voice Call

Scenario: As a valid user I can log in to Skype and make a voice call
Given I am logged in as a tester
And I am on the Skype Home Screen

When I press “Contacts”
And I wait for “Echo” to appear
And I touch the “Echo” text
And I wait for “Voice call” to appear
And I press “Voice call”
And I wait for “00:05” to appear
And I take a screenshot
And I press image button number 3
And I go back
And I go back
And I press “Sign out”
And I press “Yes”

Now run it! You just created another custom step using Ruby code. To be really creative and productive you should understand all the .rb file of the pre defined steps. All of them can be modified to satisfy your customization needs. So study them.
These files are usually located under
Ruby/Gems/1.9/gems/calabash-android-0.4.2/lib/calabash-android/steps
under this folder you should find these files. Study them!
additions_manual_steps.rb
app_steps.rb
assert_steps.rb
check_box_steps.rb
context_menu_steps.rb
date_picker_steps.rb
enter_text_steps.rb
l10n_steps.rb
list_steps.rb
location_steps.rb
map_steps.rb
navigation_steps.rb
press_button_steps.rb
progress_steps.rb
rotation_steps.rb
screenshot_steps.rb
search_steps.rb
spinner_steps.rb
time_picker_steps.rb

Level three: use your custom steps file in any test
The two levels above will only affect your current folder/project. To use your custom steps in any of your test, you can just put them in .rb file, and then include (require) them in your xxx_steps.rb in your test folder.
using examples from level 0, 1, 2 above, you can create a login.rb and put it in a location dedicated for Calabash rb files.
for example, in
/Users/your_user_name/rbfiles
folder, your create a login.rb file

require 'calabash-android/calabash_steps'

Given /^I am logged in as a tester$/ do
macro ‘I wait for “Sign in” to appear’
macro ‘I enter “tester.skype44” into input field number 1’ #replace my_username with your real username
macro ‘I enter “ts44pass” into input field number 2’ #replace with real password.
macro ‘I press the “Sign in” button’
macro ‘I press the “Enable” button’
macro ‘I wait for “Contacts” to appear’
end

now, you can use “Given I am logged in as a tester” in many test folders as long as your “require” it in your features/step_definitions/xxx_steps.rb
such as this

require 'calabash-android/calabash_steps'
require '/Users/jfang/development/test/login'

When …. #now you can use “Given I am logged in as a tester” here

And your .feature file is not affect.

Level four: Best part! Java level customization! write your own server side code

In order to do this, you should understand how Calabash-Android works. Under the hood, Calabash communicates with a tiny http server it installed to your device. On that server side, it uses Robotium to interact with your app. Calabash is just the language that makes the communication easy to understand in plain language. If there is something your Calabash can not do but Robotium can do, you can white your own server side code and tell Calabash what sentense to use in order to do it. Let’s do an example.

Notice Calabash-android does not mimic phone rotation landscape or portrait, which often cause UI problems or even crash. In its document
https://github.com/calabash/calabash-android/blob/master/ruby-gem/lib/calabash-android/canned_steps.md
it says this about rotation:

Rotation
These steps do nothing if you run them locally. If you run the test on LessPainful they will actually rotate the physical device.

Then /^I rotate the device to landscape$/
Then /^I rotate the device to portrait$/

However, Robotium IS able to set device landscape or portrait, you can find this method in Robotium doc

public void setActivityOrientation(int orientation)

So, we will write the Calabash server side code to set the screen to landscape, and create custom step to do it in Calabash! (for portrait just do the same thing)

1) First, you need to clone the Calabash-android source code, follow the instruction at
https://github.com/calabash/calabash-android/wiki/Building-calabash-android

2) Then, using text editor or your IDE, add the java file to the source code. For our example, I added SetLandscape.java under this folder:
calabash-android/ruby-gem/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures

The code is:

package sh.calaba.instrumentationbackend.actions.gestures;

import com.jayway.android.robotium.solo.Solo;

import android.view.Display;
import sh.calaba.instrumentationbackend.InstrumentationBackend;
import sh.calaba.instrumentationbackend.Result;
import sh.calaba.instrumentationbackend.actions.Action;

public class SetLandscape implements Action {

@Override
public Result execute(String… args) {

InstrumentationBackend.solo.setActivityOrientation(Solo.LANDSCAPE);
return Result.successResult();
}

@Override
public String key() {
return “set_landscape”;
}
}

Have you been wondering what is those performAction (‘xxxx’, ‘xxx’, xx) is? It is the Ruby code to send the “key” to the server. I will show you how to use it in a bit.

3) build Calabash that includes your own java code
You need Android SDK level 16 installed as well as bundler and rake. To build and install the gem go to the ruby-gem folder run rake install

cd ruby-gem
rake build

if your build is successful you should see something like this

BUILD SUCCESSFUL
Total time: 25 seconds
calabash-android 0.4.3.pre7 built to your/git/folder/calabash-android/ruby-gem/pkg/calabash-android-0.4.3.pre7.gem.

Then you can install the new gem you just built
sudo gem install /your/calabash/android/gitfolder/calabash-android/ruby-gem/pkg/calabash-android-0.4.3.pre7.gem

4) Add custom step definition.

You really should add this custom step in a .rb file in the git folder and build it together with the java code. If you add it in a exsiting .rb file, you don’t need to even add the “require” in the files that will use it.

But I am being lazy here by adding it in test folder only. In your features/step_definitions/xxx_steps.rb or any .rb file in your dedicated Calabash folder (you learn this in level 2 and 3 above), just add

Then /^I set the screen to landscape$/ do
performAction('set_landscape')
end

now when your Calabash script says “Then I set the screen to landscape”, it will performAction(‘set_landscape’), which sends the key ‘set_landscape’ we defined ourself to the tiny http server on your device, which in turn runs the Robotium (java/android) code to set the phone to landscape! This tutorial of level 4 might look simple, but there are so many things could go wrong in every step, so hit me with questions when you get stuck.

Now A picture is worth a thousand words! Then a video is worth a hundred thousand words! we will do the old routine and run Skype and call Echo and take a picture, but I will set the phone landscape once log in without touching it and make sure everything works just fine in this mode. (Note the phone was in portrait mode during sign in).

and…don’t steal my test account password 🙂 The only benefit from it is sending me messages on Skype. There is on other contacts in it.

Afterthoughts: What if I want something even Robotium can’t do? git clone and modify Robotium!

Robotium is also a great open source project, which mean you can pull the source code and modify or extend it as well. Robotium uses android.test.ActivityInstrumentationTestCase2. Android unit test. So you can go from there and extend Robotium. Things will be a bit complicated afterwards because you need to create the jar file and replace the robotium jar file in your Calabash ruby-gem. But it can be done. I will write a tutorial…if there is a need.

Another thought, Robotium vs Calabash?
As you see above something is missing in the translation. It can be done but is just not done yet. So Calabash can do less than Robotium. However it is easy to understand easy to use; the script management is also easy. Even if you need to write some Ruby code, it is still easier than writing Java.

Robotium is powerful but the test suite management is not as easy. Tests in one project file run alphabetically so you have to play with their names for correct order. And as the number of test projects grow your IDE looks really messy.

So Robotium or Calabash? Both! Both are still being improved and more is coming. In the meanwhile, go back to the basics sometimes. Use android.test.ActivityInstrumentationTestCase2 when neither of these satisfy your needs.

Update: This blog is outdated because Robotium already implemented the screen orientations. However, the method here still works. You can use some different name to try it out.

Advertisements

15 thoughts on “Four levels of customization in Calabash-Android

  1. rake aborted!
    There was a Errno::ENOENT while loading calabash-android.gemspec:
    No such file or directory – git ls-files from
    C:/Users/sai/Documents/GitHub/calabash-android/ruby-gem/calabash-android.gemspec:14:in “’
    C:/Users/sai/Documents/GitHub/calabash-android/ruby-gem/Rakefile:53:in `’
    (See full trace by running task with –trace)

    i get this error when running rake build

    • After doing a git clone don’t forget to run:
      git submodule init
      git submodule update
      Otherwise you will get the errors above.

      Also delete the folders in your “test_servers” before running your tests after installing your new gem.

  2. Great example fangmobile, this has helped me test for my android app. However i’m confused on the rb files. Basically lets say I have two buttons when the app starts “Hello” and “Help” for example, all I have to write in the my_first.feature files is ‘When I press “Hello”‘ and it works, should I be adding code to the calabash_steps.rb file? If so why? Like I have seen on git hub this – ‘

    Given /^I press the “([^\”]*)” button$/ do |buttonText|
    performAction(‘press_button_with_text’, buttonText)
    end

    should I be using this? I’m new to this so it has confused me a bit :’)

    Thanks

    • Justin,
      All you need to do is “write in the my_first.feature files is ‘When I press “Hello”‘ and it works”.
      This tutorial is about customize your steps. Since your “push button” step is standard and does not need any customization, you don’t need to change anything in calabash_steps.rb.

  3. Hi,

    I followed your steps:
    1, Clone the Calabash-android source code
    2, I added Wifi.java under this folder:
    C:\Ruby193\lib\ruby\gems\1.9.1\gems\calabash-android-0.4.18\test-server\instrumentation-backend\src\sh\calaba\instrumentationbackend\actions\wifi

    The code is:

    package sh.calaba.instrumentationbackend.actions.wifi;

    import android.content.Context;
    import android.net.wifi.WifiManager;
    import android.util.Log;
    import sh.calaba.instrumentationbackend.InstrumentationBackend;
    import sh.calaba.instrumentationbackend.Result;
    import sh.calaba.instrumentationbackend.actions.Action;

    public class Wifi implements Action {

    @Override
    public Result execute(String… args) {
    String command = args[0];
    WifiManager wifiManager = (WifiManager) InstrumentationBackend.solo.getCurrentActivity().getSystemService(Context.WIFI_SERVICE);

    if(command.equals(“enable”))
    {
    if(wifiManager.isWifiEnabled())
    Log.d(“WIFI”, “Already enabled”);
    else
    {
    wifiManager.setWifiEnabled(true);
    Log.d(“WIFI”, “Enabled”);
    }
    }

    if(command.equals(“disable”))
    {
    if(!wifiManager.isWifiEnabled())
    Log.d(“WIFI”, “Already disabled”);
    else
    {
    wifiManager.setWifiEnabled(false);
    Log.d(“WIFI”, “Disabled”);
    }
    }

    return Result.successResult();
    }

    @Override
    public String key() {
    return “status_wifi”;
    }
    }

    3) Build Calabash that includes my own java code
    cd C:\Ruby193\lib\ruby\gems\1.9.1\gems\calabash-android-0.4.18>
    then run “rake build”

    I got message

    BUILD SUCCESSFUL
    Total time: 17 seconds
    calabash-android 0.4.18 built to pkg/calabash-android-0.4.18.gem.

    But the issue is: I cannot run sudo command to install the new gem I just built
    I using Window 7 64bit

    I also Add custom step definition on folder
    C:\Ruby193\lib\ruby\gems\1.9.1\gems\calabash-android-0.4.18\lib\calabash-android\steps
    with file: wifi.rb
    and the code is:
    Then /^I turn wifi “([^\”]*)”$/ do |text|
    performAction(‘status_wifi’, text)
    end

    When I run “calabash-android run xxx.apk”
    I got the message
    ” Then I turn wifi “enable” # calabash-android-0.4.18/lib/calabash-android/steps/wifi_steps.rb:1
    Step unsuccessful: Could not find implementation for: ‘status_wifi’ (Runti
    meError)
    C:/Ruby193/lib/ruby/1.9.1/timeout.rb:68:in `timeout’
    features\my_first.feature:6:in `Then I turn wifi “enable”‘

    Did I do something wrong? Can you help me resolving this problem
    Thanks in advance.
    If possible, can you help me directly by Skype: harano1109
    I would be very grateful to you

    • Hello Vu,
      You will need to install the gem you built, otherwise it is not used.
      I use MAC so I don’t know how to install on Windows.
      Ask the Internet for the answer, install the gem, and I think you will be good.

  4. Hi,

    In case, my apps is localize (i.e. Chinese apps), so how to find the button id contain the “Chinese words”, ?

    Thanks,

    • You can add a # language: header on the first line of a feature file tells Cucumber what spoken language to use – for example # language: zh-CN for Simplified Chinese, zh-TW for traditional Chinese. If you omit this header, Cucumber will default to English (en).
      e.g.
      # language: zh-CN
      功能: 登录

      场景: 无效密码
      假定在登录页面

      use
      cucumber –i18n zh-CN
      to list the keywords of a language.

  5. I ran the steps but when I run “rake build” receives this message:

    Rake Aborted
    Errno::ENOENT: No such file or directory – ant.bat clean package -debug -Dtools.
    dir=”C:/Android/sdk/build-tools/17.0.0″ -Dandroid.api.level=19 -Dversion=0.5.2

    Anyone know how to solve?
    grateful

  6. I ran the steps but when I run “rake build” receives this message:

    Rake Aborted
    Errno::ENOENT: No such file or directory – ant.bat clean package -debug -Dtools.
    dir=”C:/Android/sdk/build-tools/17.0.0″ -Dandroid.api.level=19 -Dversion=0.5.2

    Anyone know how to solve?
    grateful

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s