Mobile Software Test Automation: Creating Android page support

Last week we generated the step definitions, and now it's time to create the page support.  If you are developing a test suite for a single platform, you won't need to set up the page objects as I am recommending.  You can simply put page specific code in the code block for each step definition.  But for those of you developing a cross-platform test suite, you must create separate page objects, as this code is platform specific and cannot be shared.

Set up Page Objects

The first thing you need to do, is in the pages folder for each platform, create a .rb file for each page or screen of the app.  You will also need to create a file for each chunk in the app, for example a popup window or slide out menu.  A good rule of thumb that I have used, if you have an assertion written to the tune of "Then I am taken to such and such page" or "Then such and such view is presented", you need a page object created for that.  It is nice to also create pages for some reusable chunks, such as a menu drawer, as well as for any sub-navigation chunks.

Inside each page object, there are a few important components.  The first line for android page support objects should be:


require 'calabash-android/abase'

for iOS page support objects, the first line should be:


require 'calabash-cucumber/ibase'

This makes sure the appropriate page objects are loaded when you run the test.  Next is your class designation. For android, it should look like this:


class YourClassNameHere < Calabash::ABase

end

Similarly, for iOS, it should look like this:


class YourClassNameHere < Calabash::IBase

end

Finally, you need to define a trait.  Essentially, this defines the page, and when you assert that you are on a specific page, this is how Calabash knows you are there.  This should be defined inside the class, so after the class designation and before the end.  It should look like this for both iOS and Android:


def trait

"Your page locator here"

end

This brings me to my next topic...

Defining element locators and creating methods

There are a few ways to find elements on the page.  You can look though source code to find the objects you are trying to interact with, but this would be challenging if you don't have the source code, or are just getting started and may not know what/where to look.  The second option is to use the interactive Calabash console to explore the application.  This is the option i will show you now.

Starting the console

To start the console, first launch the terminal.  Then, make sure you are in your project directory.  Now, you will run the following command, making sure that the path you enter is the path to the apk for the app you want to explore:


bundle exec calabash-android console ~/Documents/Nick_Personal/Projects/CalculatorSample/android-apks/Calculator.apk -p android


Keep in mind that your apk location will differ from mine.  Also, there is no return between the word "console" and the apk path, only a space.

If you built the apk from Android Studio, then pressing enter will generate a test server and start up your interactive ruby shell (irb).  However, if you pulled the apk from your device as I showed you in the sample project post, you will get an error message saying the app wasn't signed with any of the available keystores.  This message will ask you to resign the app and will actually give you the code snippet to run to resign the app. Simply run the code snippet, and then re-run the above snippet to generate a test server and start the irb shell.  

Console commands

There are a few commands you will need to run before we get started.  So once in the irb shell, your terminal should look like this:

irb_calabash_console

Now the next command you will need to run within the console is:


$ reinstall_apps

This command will actually uninstall the apk and then reinstall it.  Then you will run:


$ start_test_server_in_background

This command will launch the app on your device/emulator.  Now it's time to start finding elements to interact with.

Exploring the Application

Now that we have the calabash irb console running, we can interact with the app.  The same locators we use to find elements in the console, Calabash uses to interact with the app.  There are quite a few advanced queries that we will use later on, but for this simple app, we really only need to know a few commands.  Those are query, touch, and wait_for_element_exists.  So first, we will use query.  In the console, type:


query("*")

This is actually just a wildcard search, which returns all the elements currently in view.  You will notice there are quite a few, even for such a simple app.  So we now need to find the elements that we will be interacting with, as well as some constants on the page so that we can assert when we are on this page.

If we were looking only for the buttons on the page, we could use: 


query("android.widget.Button")

This will return all the elements with a class of android.widget.Button.  However, there are still quite a few buttons on this page, so this search is not specific enough to interact with an object.  We could then refine this more by using:


query("android.widget.Button id:'button9'")

This will return the number 9 button.  However, there are a few queries to find this element, so I will show a few:


query("button id:'button9'")

query("* id:'button9'")

query("* text:'9'")
query("button {text CONTAINS '9'}")

query("* marked:'9'")

All of these queries return the hash representing the same button object.  Sometimes you have to get creative to find a specific object, especially if the mobile developer is utilizing a recyclerView of some sort.  Thankfully in this project, that's not the case.  But I'm hoping that you can see for each query, there is an order.  The first thing we look for is the class, sometimes designated by a wildcard, others by the full class name, or a class name shortcut.  Then we can have a prop:val statement, or even NSPredicate filtering.  For now, I will stick mainly with the prop:val statements, and arguably the most used: marked.  Marked, especially when paired with a wildcard class search, is an extremely powerful search option.  You are basically saying, look for all classes with an id, contentDescription, or text that you specify, which makes finding objects much, much easier.

So continuing with the calculator app as an example, I see a title of "Calculator" at the top of the page, and I will try to find this element on the screen.  I could scan through all 50 elements that were returned with the wildcard query, but that would take forever, and there are better ways, as we just learned.  Instead, we can use the powerful prop:val search with marked:


query("* marked:'Calculator'")

Here is what was returned:

Since only one object was returned, this is going to be our locator that will serve as our trait on the landing page.  You should enter the section inside the parenthesis as your trait.  It will look like this:

Now that we know how to find elements, we will learn how to interact with them.  Which brings us to the next calabash command: touch.  First, use the same query method above to find the buttons you want to interact with.  For our sample problem, we will do 10 plus 9, meaning we need to find the 0, 1, 9, plus, and equals buttons.  Once you find all the buttons, then we can interact with them.  Instead of using query(), we will use touch():


touch("* marked:'9'")


Running this will touch the displayed 9 button on your device.  You can do the same for the other buttons we need.  Now, on to defining these in code.  

To keep in line with best practices, and to make the code easier to maintain, we will break these methods up into chunks.  So first, we will define all the buttons with their locators as follows:

Now, we can define our touch methods and include the locators.  Also, using the same query method as before, we want to find our locator for the answer.  The only difference is, we will use this with the calabash command: wait_for_element_exists.  This is a boolean (true or false) method that will wait a predefined amount of time for the object to appear.  

When it is all done, it should look like this:

Pretty simple, right?  I only went over a few of the locator strategies now, because I didn't want to make this post any longer than it already was.  If you are looking for more information on this now, you can go here.  Otherwise, I will take a more in-depth look at locator strategies later on.

Well thats it for today folks.  You should have learned how to: start the calabash console, a few basic queries, touch events, and finally coding some page support!  Next week, we will plug this code into our step definitions and run the project, so stay tuned!