Appium Bootcamp – Chapter 5: Writing and Refactoring Your Tests

Sauce AI for Test Authoring: Move from intent to execution in minutes.|xBack to ResourcesBlogPosted

January 06, 2026 · 14 min read · Tool Comparison

Sauce AI for Test Authoring: Move from intent to execution in minutes.

|

x

Back to Resources

Blog

Posted August 5, 2014

Appium Bootcamp – Chapter 5: Writing and Refactoring Your Tests

quote

[UPDATE- November 2019]You can find the novel Get Started with Appium white papers here:Get Started with Appium- Java and Get Started with Appium- Ruby

This is the fifth post in a series telephone Appium Bootcamp by noted Selenium expertDave Haeffner. 

Dave lately immerse himself in the open germ Appium project and cooperate with leading Appium contributor Matthew Edwards to bring us this material. Appium Bootcamp is for those who are brand new to wandering exam automation with Appium. No familiarity with Selenium is require, although it may be utilitarian. This is the fifth of eight posts; two new station will be released each workweek.

Now that we & # x27; ve identified some trial action in our apps, let & # x27; s put them to act by wiring them up in code.

We & # x27; ll start with the iOS app and then locomote onto Android. But maiden, we & # x27; ll need to do a quick bit of setup.

Quick Setup

Since we & # x27; re setting up our test code from scratch, we & # x27; ll take to make sure we have the necessary gems installed -- and done so in a way that is quotable (which will arrive in handy for other team appendage and for use with Continuous Integration).

In Ruby, this is leisurely to do withBundler. With it you can determine a list of gem and their variation to instal and update from for your project.

Install Bundler by runninggem install bundlerfrom the command-line and then make a file calledGemfilewith the following contents:

# filename: Gemfile

source & # x27; https: //rubygems.org & # x27;

gem & # x27; rspec & # x27;, & # x27; ~ & gt; 3.0.0 & # x27;
gem & # x27; appium_lib & # x27;, & # x27; ~ & gt; 4.0.0 & # x27;
gem & # x27; appium_console & # x27;, & # x27; ~ & gt; 1.0.1 & # x27;

After make theGemfile run bundle install. This will make certainrspec(our testing framework),appium_lib(the Appium Ruby bindings), andappium_console(our interactive test console) are instal and ready for use in this directory.

Capabilities

In order to run our tests, we will need to delineate the capabilities of our app. We can either do this in our test codification, or we can leverage theappium.txtfiles we used for the Appium Console.

Let & # x27; s do the latter approaching. But initiatory, we & # x27; ll want to make two new folders; one for Android and another for iOS. Once they & # x27; re created, let & # x27; s place each of theappium.txtfile into their several folders.

├── Gemfile
├── Gemfile.lock
├── android
│ └── appium.txt
└── ios
└── appium.txt

Be sure to update theappcapability in yourappium.txtfiles if you & # x27; re employ a relative path.

Writing Your Initiatory Test

With our initial frame-up taken aid of, let & # x27; s make our inaugural test file (a.k.a. & quot; spec & quot; in RSpec). The test actions we name in the old post were concentre on piloting in the app. So let & # x27; s ring this specification filenavigation_spec.rband place it in theios folder.

├── Gemfile
├── Gemfile.lock
├── android
│ └── appium.txt
└── ios
└── appium.txt
└── navigation_spec.rb

Now let & # x27; s write our test to launch Appium for iOS and perform a uncomplicated navigation test.

In RSpec, describerefer the beginning of a test file, whereasitannounce a test. So what we feature is a test file with a individual tryout in it.

In this test file, we are part our Appium session before each tryout (e.g.,before (: each)) and cease it after each tryout (e.g.,after (: each)). More specifically, inbefore (: each), we are finding the route to the iOSappium.txtfile and then loading it. After that we start the Appium session and promote the Appium command so they will be available for use within our test. We so supplydriver_quit in after (: each)to flawlessly end the Appium session. This is equivalent to submitting anxcommand in the Appium console.

The bidding in our exam (it & # x27; First cell & # x27; do) should seem conversant from the last billet. We & # x27; re happen the initiatory cell, grab it & # x27; s title, click on the cell, and then looking to see if the title appeared on the inner blind.

After saving this file, let & # x27; s change directories into theiosfolder (e.g.,cd ios), and run the exam (assuming your Appium Server is running -- if not, charge up the Appum GUI and clickLaunch) with rspec navigation_spec.rb. When it & # x27; s running, you will see the iOS simulator launch, lade up the test app, tick the first cell, and so close.

This is a good showtime, but we can clean this code up a bit by leveraging some simple page objects and a central configuration.

A Page Objects Primer

Machine-driven examination can quickly turn brittle and hard to hold. This is largely due to the fact that we are testing functionality that will constantly change. In order to battle this, we can use page target.

Page Objects are simple objects that model the behavior of an covering. So instead than write your tests directly against your app, you can indite them against these aim. This will make your test codification more reusable, maintainable, and leisurely to fix when the app changes.

You can learn more about page objectshere and here.

Refactoring Your First Test

Let & # x27; s create a new directory calledpageswithin ouriosdirectory and create two new file in it:home.rb and inner_screen.rb. And while we & # x27; re at it, let & # x27; s create a new folder to store our test file (namespec-- which is a brochure RSpec will know to look for at run clip) and go ournavigation_spec.rb into it.

├── Gemfile
├── Gemfile.lock
├── android
│ └── appium.txt
└── ios
├── appium.txt
├── pages
│ ├── home.rb
│ └── inner_screen.rb
└── spec
├── navigation_spec.rb

Let & # x27; s open upios/pages/home.rbto create our first page aim.

# filename: ios/pages/home.rb

faculty Pages
module Home
course & lt; & lt; self

def first_cell
@ found_cell = waiting {text 2}
self
end

def title
@ found_cell.name.split (& # x27;, & # x27;) .first
end

def click
@ found_cell.click
end

end
end
end

module Kernel
def home
Pages: :Home
end
end

Since the Appium commands are getting advertise for use (instead of passing around a driver object), storing our page objects in a faculty is a light approach (instead than keeping them in a class that we would want to instantiate).

To create theHomemodule we foremost wrap it in another module calledPages. This helps prevent any namespace collisions as well simplify the promotion of Appium methods.

In Home, we & # x27; ve created some simple static methods to mime the demeanor of the home screen (e.g.,first_celltitleclick). By storing the found cell in an instance variable (e.g.,@ found_cell) and returningself, we will be able to chain these method together in our test (e.g.,first_cell.title). And in order to cleanly cite the page object in our test, we & # x27; ve made thehomemethod useable globally (which cite this module).

SUSA automates exploratory testing with persona-driven behavior, catching bugs that scripted automation misses.

Now let & # x27; s open upios/pages/inner_screen.rband make our second page object.

# filename: pages/inner_screen.rb

faculty Pages
module InnerScreen
class & lt; & lt; self

def has_text (textbook)
postponement {text_exact textbook}
end

end
end
end

module Kernel
def inner_screen
Pages: :InnerScreen
end
end

This is the like structure as our previous page object. In it, we & # x27; re performing an accurate text search.

Let & # x27; s go ahead and update our test to use these page objects.

# filename: ios/spec/navigation_spec.rb

require & # x27; appium_lib & # x27;
require_relative & # x27; .. /pages/home & # x27;
require_relative & # x27; .. /pages/inner_screen & # x27;

describe & # x27; Home Screen Navigation & # x27; do

before (: each) do
appium_txt = File.join (Dir.pwd, & # x27; appium.txt & # x27;)
caps = Appium.load_appium_txt file: appium_txt
Appium: :Driver.new (caps) .start_driver
Appium.promote_appium_methods RSpec: :Core: :ExampleGroup
Appium.promote_singleton_appium_methods Pages
end

after (: each) do
driver_quit
end

it & # x27; First cell & # x27; do
cell_title = home.first_cell.title
home.first_cell.click
inner_screen.has_text cell_title
end

end

We foremost take the page objects (note the use ofrequire_relativeat the top of the file). We so promote the Appium methods to our page objects (e.g.,Appium.promote_singleton_appium_methods Pages). Lastly, we update our test.

Now when we run our test from within theiosdirectory (e.g.,cd ios then rspec) so it will run just the same as it did before.

Now the test is more readable and in best figure. But there is still some refactoring to do to labialise things out. Let & # x27; s draw our test apparatus out of this test file and into a central config that we will be capable to leverage for both iOS and Android.

Central Config

In RSpec, we can configure our test suite from a primal location. This is typically done in a file ringspec_helper.rb. Let & # x27; s create a leaflet calledcommonin the base of our task and add aspec_helper.rbfile to it.

├── Gemfile
├── Gemfile.lock
├── android
│ └── appium.txt
├── common
│ └── spec_helper.rb
└── ios
├── appium.txt
├── pages
│ ├── home.rb
│ └── inner_screen.rb
└── spec
├── navigation_spec.rb

Let & # x27; s open upcommon/spec_helper.rb, add our examination setup to it, and smoothen it up.

# filename: common/spec_helper.rb

require & # x27; rspec & # x27;
require & # x27; appium_lib & # x27;

def setup_driver
return if $ driver
caps = Appium.load_appium_txt file: File.join (Dir.pwd, & # x27; appium.txt & # x27;)
Appium: :Driver.new caps
end

def promote_methods
Appium.promote_singleton_appium_methods Pages
Appium.promote_appium_methods RSpec: :Core: :ExampleGroup
end

setup_drive
promote_methods

RSpec.configure do |config|

config.before (: each) do
$ driver.start_driver
end

config.after (: each) do
driver_quit
end

end

After requiring our requisite libraries, we & # x27; ve created a couple of methods that get executed when the file is loaded. One is to setup (but not part) Appium and another is to promote the method to our page objects and exam. This access is taken to get sure that only one case of Appium is loaded at any one clip.

We then configure our tryout activeness so they run before and after each examination. In them we are commence an Appium session and then cease it.

In order to use this central config, we will need to require it (and remove the unnecessary minute) in our test.

# filename: ios/spec/navigation_spec.rb

require_relative & # x27; .. /pages/home & # x27;
require_relative & # x27; .. /pages/inner_screen & # x27;
require_relative & # x27; .. / .. /common/spec_helper & # x27;

describe & # x27; Home Screen Navigation & # x27; do

it & # x27; First cell & # x27; do
cell_title = home.first_cell.title
home.first_cell.click
inner_screen.has_text cell_title
end

end

Note the order of therequire_relativestatement --they are significant. We need to load our page objects before we can load ourspec_helper, or else the test won & # x27; t run.

If we run the test from within theiosdirectory withrspec, we can see everything execute just like it did before.

Now that we have iOS extend, let & # x27; s wire up an Android test, some page objects, and do certain our test codification to indorse both device.

Including Android

It & # x27; s worth noting that in your real world apps you may be capable to receive a individual set of test and segmented page aim to help make things run seamlessly behind the scenes for both device. And while the behavior in our Android exam app is similar to our iOS test app, it & # x27; s design is different plenty that we & # x27; ll need to create a separate test and page objects.

Let & # x27; s start by creatingspec and pagesfolders within theandroiddirectory and then creating page objects inpages (e.g., home.rb and inner_screen.rb) and a test file inspec (e.g., navigation_spec.rb).

├── Gemfile
├── Gemfile.lock
├── android
│ ├── appium.txt
│ ├── Page
│ │ ├── home.rb
│ │ └── inner_screen.rb
│ └── spec
│ ├── navigation_spec.rb
├── common
│ └── spec_helper.rb
└── ios
├── appium.txt
├── pages
│ ├── home.rb
│ └── inner_screen.rb
└── spec
├── navigation_spec.rb

Now let & # x27; s open and populate our page objects and test file.

faculty Pages
module Home
class & lt; & lt; self

def first_cell
@ found_cell = wait {text 2}
self
end

def click
@ found_cell.click
end

end
end
end

faculty Kernel
def home
Pages: :Home
end
end

This page aim is like to the iOS one except there & # x27; s no rubric search (since we won & # x27; t be needing it).

module Pages
module InnerScreen
class & lt; & lt; self

def has_text (schoolbook)
waiting {find_exact text}
end

end
end
end

module Kernel
def inner_screen
Pages: :InnerScreen
end
end

In this page object we & # x27; re performing a search for an element by textbook (similar to the iOS example), but utilise find_exact instead of text_exact because of how the app is project (we involve to perform a broader hunt that will search across multiple attributes, not exactly the text attribute). Now let & # x27; s telegraph up our test.

require_relative & # x27; .. /pages/home & # x27;
require_relative & # x27; .. /pages/inner_screen & # x27;
require_relative & # x27; .. / .. /common/spec_helper & # x27;

describe & # x27; Home Screen Navigation & # x27; do

it & # x27; First cell & # x27; do
home.first_cell.click
inner_screen.has_text & # x27; Accessibility Node Provider & # x27;
end

end

Now if we cd into the androiddirectory and run our tryout withrspecit should launch the Android aper, load the app, click the first cell, and then end the session. The emulator will remain open, but that & # x27; s something we & # x27; ll address in a future post.

One More Thing

If we use the console with the code that we have flop now, we won & # x27; t be able to cite the page object we & # x27; ve created -- which will be a bit of a pain if we require to cite them when debug test failure. Let & # x27; s fix that.

Let & # x27; s make a new file in ourandroid/spec and ios/specdirectories callrequires.rb. We & # x27; ll move our require statements out of our trial files and into these files instead.

├── Gemfile
├── Gemfile.lock
├── android
│ ├── appium.txt
│ ├── pages
│ │ ├── home.rb
│ │ └── inner_screen.rb
│ └── spec
│ ├── navigation_spec.rb
│ └── requires.rb
├── common
│ └── spec_helper.rb
└── ios
├── appium.txt
├── pages
│ ├── home.rb
│ └── inner_screen.rb
└── spec
├── navigation_spec.rb
└── requires.rb

Here & # x27; s what one of them should look like:

# filename: ios/spec/requires.rb

# require the ios pages
require_relative & # x27; .. /pages/home & # x27;
require_relative & # x27; .. /pages/inner_screen & # x27;

# setup rspec
require_relative & # x27; .. / .. /common/spec_helper & # x27;

Next, we & # x27; ll require to update our tests to use this file.

require_relative & # x27; requires & # x27;
describe & # x27; Home Screen Navigation & # x27; do
it & # x27; First cell & # x27; do
cell_title = home.first_cell.title
home.first_cell.click
inner_screen.has_text cell_title
end

end

# filename: android/spec/navigation_spec.rb

require_relative & # x27; requires & # x27;

describe & # x27; Home Screen Navigation & # x27; do
it & # x27; First cell & # x27; do
home.first_cell.click
inner_screen.has_text & # x27; Accessibility Node Provider & # x27;
end

end

Now that we hold a keyrequires.rbfor each twist, we can narrate the Appium Console to use it. To do that, we & # x27; ll need to add some extra info to ourappium.txt files.

# filename: ios/appium.txt

[caps]
deviceName = & quot; iPhone Simulator & quot;
platformName = & quot; ios & quot;
app = & quot; .. / .. / .. /apps/UICatalog.app.zip & quot;

[appium_lib]
require = [& quot; ./spec/requires.rb & quot;]
# filename: android/appium.txt

[caps]
platformName = & quot; android & quot;
app = & quot; .. / .. / .. /apps/api.apk & quot;
avd = & quot; training & quot;
deviceName = & quot; Android & quot;

[appium_lib]
require = [& quot; ./spec/requires.rb & quot;]

This new requirevalue is solely used by the Appium Console. Now if we runarcfrom either theios or androiddirectory, we & # x27; ll be able to access the page objects just like in our tests.

And if we run our examination from either directory, they will notwithstanding work as point.

Outro

Now that we feature our tests, page objective, and primal configuration all sorted, it & # x27; s time to look at wrapping our test execution and make it so we can run our tryout in the cloud.

About Dave Haeffner: Dave is a recent Appium convert and the author of Elemental Selenium (a free, once weekly Selenium tip newssheet that is read by thousands of screen professionals) as well as The Selenium Guidebook (a step-by-step guide on how to use Selenium Successfully). He is also the creator and sustainer ofChemistryKit(an open-source Selenium framework). He has facilitate numerous companies successfully implement automated acceptance test; including The Motley Fool, ManTech International, Sittercity, and Animoto. He is a founder and co-organizer of theSelenium Hangoutand has spoken at legion conferences and meetups about acceptance examination.

Follow Dave on Twitter -@ tourdedave

Continue the indication the early chapters:

Published:
Aug 5, 2014
Share this billet
Copy Share Link
LinkedIn
© 2026 Sauce Labs Inc., all right reserved. SAUCE and SAUCE LABS are registered trademarks possess by Sauce Labs Inc. in the United States, EU, and may be registered in other jurisdictions.
robot
quote

Automate This With SUSA

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts needed.

Try SUSA Free

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free