End to End Testing with Appium

Reading Time : 14min read
End to end testing with appium

Today, we’re diving into end to end testing with Appium, helping you apply all your knowledge in an “end-to-end” test. At times, we’ll reference previous articles, and we’ll also rehash some information we’ve previously covered. Seeing coverage from another angle will help solidify your knowledge.

This article is divided into 4 sections:

  1. Setting up Appium.
  2. Test Planning.
  3. Test Setup.
  4. Test Case Writing and Execution.

1. Setting up Appium:

This is a section we won’t repeat here as it was covered extensively in a previous article. Setting up environments isn’t ever any fun, but once it’s setup, you’re good to go. We’re going to assume you have everything setup – and refer to the previous article if you need some help.

2. Test Planning:

As the expression goes – “Measure twice, cut once”. Some planning up-front is going to save you a lot of headache later on. And good test planning means a little experimentation with manual testing before starting automation. Specifically:

  1. The first thing you need to check is application compatibility or suitability for automation testing. You can check that by verifying the values of selectors using the locator inspection tool (Appium accessibility inspector or UiAutomator). You can quickly assess the locators of basic UI elements such as username field, password field, login button etc. and if you don’t find unique locators for them, you’re going to need to use Xpath, which is less than ideal. If at all possible, work with the development team to see if they can assign unique locators to UI elements.
  2. After verifying the availability of unique locators, you need to explore the whole application thoroughly. You need to understand each and every feature of the app, and prepare the list of the most important ones. After preparing the list, you can prepare manual test cases. With automation and parallel execution, keep in mind that your test could be executed in a random order, at any point of time, so it should be very granular. Also, it is important that the test cases you design are modular and independent.

3. Test Environment Setup:

In order to perform automation using Appium, you need to:

  1. Write the code which will find the UI element on the screen.
  2. After getting the element, write the code which will perform an action upon it.
  • This is all done within your test code – the Appium server will interact with the application artifacts(.ipa or .apk) and your test code:
Figure-1: Appium Process. So you You can create a native mobile application on XCode(for iOS) and Android Studio(for Android) OR you can also use the build (.ipa/.apk) directly, write the UI test cases in your preferred programming language and executes them manually from the IntelliJ IDEA, Eclipse IDE, Visual Studio or IntelliJ PHPStorm. Moreover, if you want to achieve end-to-end automation and want UI test cases as the part of your CI/CD process, then you can also integrate them with tools such as Jenkins and BitBucket. First, you need to get the build(.ipa/.apk), choose the programming language for your automation code, and get the physical device/Simulator/Emulator for testing.

4. Test Case Writing:

After designing the test cases and setting up the test environment, you can start writing the test cases. For illustrative purposes, we will be automating the Android messaging app from Google. To reemphasize best practices, we will utilize the page object model to create a cleaner and more modular solution. Because we already discussed the page object model framework in detail previously, we can save time by cloning the automation framework project we used previously, which you can find on github here.

Now, let’s discuss the scenario which we want to automate. We have taken Google message application(v 3.9.039) for the automation.

But before starting the automation, we will need to plan our test. So as per our above discussion, we need to take care of 2 things.

  1. We need to quickly check the app feasibility for automation, and to do so, we need to verify that the locators of the app are unique. Here, we will use uiautomatorviewer to find the unique locators of UI elements, as it is quick on Android compared to the Appium inspection tool.
Figure-2: Quick check of locators on messaging app.

As you can see above, the button has a unique resource-id – The Google developers have assigned unique ids to all UI elements, so the first step is clear.

  1. The Google message app is straightforward – you can explore the whole application and can get an idea of each feature. As the application is mainly designed to send an SMS to contacts, we will need to automate the most important scenario, which is sending the SMS to a particular contact. Here are the manual steps we will perform and then use this to create the automation:
    1. Open the google message application. An app should open.
    2. Tap on ‘Start chat’ OR ’+’ button. New conversion screen should appear.
    3. Tap on the suggested contact below search box.(like ‘Send to 111-1111’) Conversation screen should appear.
    4. Tap on the suggested contact below search box.(like ‘Send to 111-1111’) Conversation screen should appear.
    5. Type into the message textfield. A message should be typed correctly.
    6. Tap on ‘SMS’ button Message should be sent to the recipient.

Now, both the conditions are satisfied, so we can move forward and start writing the automation test case. We will use the page object model framework for writing the automation test case and using it involves just a few steps. It may seem like overkill for this test case, but we’re putting in a good baseline for a larger more sophisticated automation project:

  1. You can get the POM-based automation framework from our GitHub page.
  2. Import as gradle project in IntelliJ IDEA/Eclipse IDE.
  3. Run the build.gradle file in order to download all dependencies.
  4. Move to the configuration.properties file and change these properties as per the connected Android device. android.platform.version=android.device.name=
  5. Set the proper desired capabilities.
  6. Get the unique locators for UI elements on the app.
  7. Create the page objects of different screens.
  8. Write the automation test case using the created page object’s methods.

The First 4 steps are straight forward – now we get to the desired capabilities part!

Set Desired Capabilities

Because the app we have chosen comes pre-installed on Android devices, we can skip the installation and go straight to opening the app.

DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, “uiautomator2”); desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, “c4e3f3cd”); desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, “Android”); desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, “8.0”); desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, “com.google.android.apps.messaging”); desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, “com.google.android.apps.messaging.ui.ConversationListActivity”); desiredCapabilities.setCapability(MobileCapabilityType.FULL_RESET, false); desiredCapabilities.setCapability(MobileCapabilityType.NO_RESET, true); desiredCapabilities.setCapability(AndroidMobileCapabilityType.AUTO_GRANT_PERMISSIONS, true);

Here, we don’t use the desiredCapabilities.setCapability(MobileCapabilityType.APP, ); capability because we are not installing the application.

Getting the unique locators

After setting the desired capabilities, you need to extract the selectors using uiautomatorviewer(or Appium inspector) for all the UI elements we need to control.

NOTE: The Google message application will change periodically so there are chances that the element IDs may change over time, so please modify the automation script accordingly.

There are typically 4 screens we need to take care of for automating our scenario:

After identifying the screens, we need to get the selectors of each element from all screens, which will be needed while automating our test case.

So, let’s make a list:

Screen Name Element Locator Messages ‘Start chat’ button ID: start_new_conversation_button Or ID: com.google.android.apps.messaging:id/start_new_conversation_button New conversation ‘To’ textfield ID: recipient_text_view New conversation ‘Send to 432-5235’ textview Not present on DOM, but we can skip this by tapping on ENTER from keyboard. Conversation SMS Message textfield ID: compose_message_text Conversation ‘SMS’ button ID: send_message_button_container Conversation Sent Message layout XPath: //android.support.v7.widget.RecyclerView/android.widget.FrameLayou

NOTE: You can use the ID selector in these formats:

  1. start_new_conversation_button
  2. com.google.android.apps.messaging:id/start_new_conversation_button

If you look at the above table closely you can notice that most of elements have unique Ids, however a few elements have some issues with their id:

  1. Send to 432-5235’ textview is not present in DOM at all so we can’t locate that element.

So, we need to look for a workaround. If you look closely at this screen than you might figure out that you just have to press the ‘correct’ (green check mark) icon on the keyboard and you don’t need to press the ‘Send to 432-5235’ textview.

We can click on the icon from the soft keyboard:

driver.pressKey(new KeyEvent().withKey(AndroidKey.ENTER));
  1. The 2nd issue is there is no unique id assigned to the sent message textview. So we need to look at another locator strategy. What about using the accessibility id locator strategy? Unfortunately that would only work if the id is static and here it keeps changing, so that strategy won’t work either.So the XPath locator strategy remains. It’s actually good practice to use the text attribute in XPath but if you see the below screenshot you can find that there is no text assigned to the sent message textview. So we will need to use XPath as follows:
@AndroidFindBy(xpath = “//android.support.v7.widget.RecyclerView/android.widget.FrameLayout”)
List sentMessageLayout;

This game of finding the right locator strategy to use is very common in test automation. You’ll explore the elements and then go through a process of trying to find what the best locator strategy is in order to reach those elements. You may be wondering why we have taken the List of AndroidElement ? This is because the problem with the single AndroidElement is if you have multiple messages sent to the same mobile number it will always take the first element but we need the last element of the sent message. Please see Figure-7: FrameLayout presents for each sent message.

Figure-6: Id is not assigned to sent message textview.
Figure-7: FrameLayout presents for each sent message. Now that we have all the unique locators which we wanted, we can start creating our actions in the PO classes.

Create Action Methods in PO Classes

We need to create action methods on Page Object classes, which will tap on buttons and fill the text fields and assert text values on the app screen.

For example, on the Message(Dashboard) screen we need a method which will tap on the ‘Start chat’ button. We already have the selector of the ‘Start button’ so we can create our method:

public class MessagesPO extends BasePO { public MessagesPO(AppiumDriver driver) { super(driver); } @AndroidFindBy(id = “com.google.android.apps.messaging:id/start_new_conversation_button”) AndroidElement startChatButton; public NewConversationPO tapOnStartChatButton() { startChatButton.click(); return new NewConversationPO(driver); } }

Why does our tapOnStartChatButton() method return a new object of NewConversationPO? The answer is that whenever any method is responsible to change the screen we can return the object of the subsequent screen’s PO class so that we don’t need to create the object of that PO class separately while writing test case.

Here, the method tapOnStartChatButton() will tap on ‘Start chat’ button which will navigate to the New Conversation screen so we are returning the object of NewConversationPO class.

This practice is not mandatory, but it is good to have.

NOTE: All PO class should extend the BasePO class as it contains the logic which initializes the page factory and other utility classes. You can look into the chapter titled: “Developing Test Automation Framework for Appium using Page Object Modeling(POM)“ for more details.

We have created the below table, which gives the mapping between screen names and corresponding method names.

Screen Name Method Name Messages tapOnStartChatButton(): It will tap on Start chat button. New conversation typeAndSubmitContactNumber(String contactNo): It will type the contact no. and submit it for conversation. Conversation typeInSMSTextField(String text): This method will type into message textfield.
tapOnSMSButton(): This method will tap on the SMS button.
isConversationScreenDisplayed(): This method is used to verification purpose, it will return true if conversation screen appears.
getLastSentMessage(): It will return the AndroidElement for last sent message.
getLastSentMessageText(): This method is used to get the last sent message text.
isMessageSent(): It will verify that message is sent or not.
isLastSentMessageContains(String subString): This method will check whether last sent message contains the passed subString or not

Create the test case and use action methods from PO classes.

This is the final and easiest, yet most powerful, step of automation test case writing. Here, you have to organize all the methods from your PO classes in order to make the complete automation test case and validate the result using assertions.

  • Assertions are fundamental in test case writing – without them you’re just doing automation, and not automated testing. Ideally, you would put as many assertions as you can. Assertions can be thought of as checkpoints. At the end of any action method you will have some expected results, and to measure those expected results you have to put assertion statements in the code.
  • In our example, we are using the TestNG assertions.
  • Assertions simply compares the value of expected and actual values. If the expected and actual values are equal then the assertion ‘passes’ and we continue with code execution. If it fails (the actual result does not match the expected result) the user defined message is thrown.

In our example, we have 2 assertions:

  1. Verify that the message has been sent to the user:

Here, we want to check that the application has sent the message and is being displayed on the conversation screen.

Assert.assertTrue(conversationPO.isMessageSent(), “Message:'” + messageText + “‘ is not being sent!”);

The 2nd parameter is the error message we want to throw, in case of failure of the assertion. A good error message should give sufficient information about what it is checking, so that your diagnostics becomes easier.

  1. Verify that sent message is as per expectations:

Checking that the message is sent is one thing, but was the right message sent? As discussed earlier, there is no unique id assigned to the sent message text view, so we can not get the text of the last message. We can, however, get the whole FrameLayout text. So, logically, we can have an assertion, which will check that the expected text is present on the last sent message or not. In our example, we have put the timestamp in milliseconds in a text message and we will verify that given timestamp text value is present on last sent message or not.

Assert.assertTrue(conversationPO.isLastSentMessageContains(timestamp), “Last sent message is different than expected!, Original message is: ‘” + conversationPO.getLastSentMessageText() + “‘, while the expected substring is: ” + timestamp + “‘”);

After organizing the action methods from the PO and adding our assertions we have our complete test case which will send the message to a particular contact number.

public class TestCases extends BaseTest { @Test public void verifyUserCanSendMessage() { final String phoneNo = “00011122233”; final String timestamp = System.currentTimeMillis() + “”; final String messageText = “Hello, there. Current time is: ” + timestamp; MessagesPO messagesPO = new MessagesPO(androidDriver); NewConversationPO newConversationPO = messagesPO.tapOnStartChatButton(); ConversationPO conversationPO = newConversationPO.typeAndSubmitContactNumber(phoneNo); Assert.assertTrue(conversationPO. isConversationScreenDisplayed(), “Conversation screen didn’t appear!”); conversationPO.typeInSMSTextField(messageText); conversationPO.tapOnSMSButton(); Assert.assertTrue(conversationPO.isMessageSent(), “Message:'” + messageText + “‘ is not being sent!”); Assert.assertTrue(conversationPO.isLastSentMessageContains (timestamp), “Last sent message is different than expected!, Original message is: ‘” + conversationPO.getLastSentMessageText() + “‘, while the expected substring is: ” + timestamp + “‘”); } @BeforeTest @Override public void setUpPage() throws MalformedURLException { androidDriver = new AndroidDriver(new URL(APPIUM_SERVER_URL), getDesiredCapabilitiesForAndroid()); } }

The full code is available on github here.

As we said at the outset, this app may have changed by the time this guide was published. A new UI could render some of our test cases invalid. We have however tried to lay out a methodical and disciplined approach to tackling any test automation project. You should be to apply this approach for any mobile application.

Appium eBook

Interested in Learning More?

Subscribe today to stay informed and get regular updates from Kobiton

Ready to accelerate delivery of
your mobile apps?

Request a Demo