Now that we've identified some test actions in our apps, let's put them to work by wiring them up in code.
We'll start with the iOS app and then move onto Android. But first, we'll need to do a quick bit of setup.
Since we're setting up our test code from scratch, we'll need to make sure we have the necessary gems installed -- and done so in a way that is repeatable (which will come in handy for other team members and for use with Continuous Integration).
In Ruby, this is easy to do with Bundler. With it you can specify a list of gems and their versions to install and update from for your project.
Install Bundler by running
gem install bundler from the command-line and then create a file called
Gemfile with the following contents:
After creating the
bundle install. This will make sure
rspec (our testing framework),
appium_lib (the Appium Ruby bindings), and
appium_console (our interactive test console) are installed and ready for use in this directory.
In order to run our tests, we will need to specify the capabilities of our app. We can either do this in our test code, or we can leverage the
appium.txt files we used for the Appium Console.
Let's do the latter approach. But first, we'll want to create two new folders; one for Android and another for iOS. Once they're created, let's place each of the
appium.txt files into their respective folders.
Be sure to update the
app capability in your
appium.txt files if you're using a relative path.
Writing Your First Test
With our initial setup taken care of, let's create our first test file (a.k.a. "spec" in RSpec). The test actions we identified in the previous post were focused on navigation in the app. So let's call this spec file
navigation_spec.rb and place it in the
Now let's write our test to launch Appium for iOS and perform a simple navigation test.
describe denotes the beginning of a test file, whereas
it denotes a test. So what we have is a test file with a single test in it.
In this test file, we are starting our Appium session before each test (e.g.,
before(:each)) and ending it after each test (e.g.,
after(:each)). More specifically, in
before(:each), we are finding the path to the iOS
appium.txt file and then loading it. After that we start the Appium session and promote the Appium commands so they will be available for use within our test. We then issue
after(:each) to cleanly end the Appium session. This is equivalent to submitting an
x command in the Appium console.
The commands in our test (
it 'First cell' do) should look familiar from the last post. We're finding the first cell, grabbing it's title, click on the cell, and then looking to see if the title appeared on the inner screen.
After saving this file, let's change directories into the
ios folder (e.g.,
cd ios), and run the test (assuming your Appium Server is running -- if not, load up the Appum GUI and click
rspec navigation_spec.rb. When it's running, you will see the iOS simulator launch, load up the test app, click the first cell, and then close.
This is a good start, but we can clean this code up a bit by leveraging some simple page objects and a central configuration.
A Page Objects Primer
Automated tests can quickly become brittle and hard to maintain. This is largely due to the fact that we are testing functionality that will constantly change. In order to combat this, we can use page objects.
Page Objects are simple objects that model the behavior of an application. So rather than writing your tests directly against your app, you can write them against these objects. This will make your test code more reusable, maintainable, and easier to fix when the app changes.
Refactoring Your First Test
Let's create a new directory called
pages within our
ios directory and create two new files in it:
inner_screen.rb. And while we're at it, let's create a new folder to store our test files (called
spec -- which is a folder RSpec will know to look for at run time) and move our
navigation_spec.rb into it.
Let's open up
ios/pages/home.rb to create our first page object.
Since the Appium commands are getting promoted for use (instead of passing around a driver object), storing our page objects in a module is a cleaner approach (rather than keeping them in a class that we would need to instantiate).
To create the
Home module we first wrap it in another module called
Pages. This helps prevent any namespace collisions as well simplify the promotion of Appium methods.
Home, we've created some simple static methods to mimic the behavior of the home screen (e.g.,
click). By storing the found cell in an instance variable (e.g.,
@found_cell) and returning
self, we will be able to chain these methods together in our test (e.g.,
first_cell.title). And in order to cleanly reference the page object in our test, we've made the
home method available globally (which references this module).
Now let's open up
ios/pages/inner_screen.rb and create our second page object.
This is the same structure as our previous page object. In it, we're performing an exact text search.
Let's go ahead and update our test to use these page objects.
We first require the page objects (note the use of
require_relative at the top of the file). We then 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 the
ios directory (e.g.,
cd ios then
rspec) then it will run just the same as it did before.
Now the test is more readable and in better shape. But there is still some refactoring to do to round things out. Let's pull our test setup out of this test file and into a central config that we will be able to leverage for both iOS and Android.
In RSpec, we can configure our test suite from a central location. This is typically done in a file called
spec_helper.rb. Let's create a folder called
common in the root of our project and add a
spec_helper.rb file to it.
Let's open up
common/spec_helper.rb, add our test setup to it, and polish it up.