iOS and Android apps automation

  • View
    528

  • Download
    5

  • Category

    Mobile

Preview:

Citation preview

iOS and Android Apps automation

By, Sridhar Ramakrishnan

Life long travel geek @ Hotwire Inc.

Agenda• How is Native app testing is different from Web testing• Kind of Native app bugs• Test automation• Different framework for native apps automation• What is appium & how it works• Android & iOS frameworks – Espresso & Xcode UI testing• Sample test code• Demo• Code Coverage• Our CI process• Screenshot testing – Image comparison tests• Tracking• So why we are doing all this??

How is Native app testing is different from Web testing

• Usability & interaction• Faster & more reactive• Involves Gestures• No unnecessary content

• Data Entry Keyboard Types• Numeric vs. Alphanumeric

• Phone Settings• Changing Regions• Changing Location Services• Changing Notifications

• Devices vs. Simulators

• Multiple Devices and Operating Systems

Bugs Types

• Crashes• Missing data from the API• Missing data in the app• Wrong keyboard displayed• Rendering differences across devices• Views overlaying one another• Views are getting locked up• Gesture related – swipe, double tap

More Bugs ?

• Limited manual regression testing = Spot check ; less boring

• More manual exploratory testing = More bugs;

“Simultaneously learning about the system while designing and executing tests, using feedback from the last test to inform the next.” - Elisabeth Hendrickson

Test Automation

Frameworks comparison

Frameworks for comparison

Test the actual

appMany

languageStandard API ( Webdriver)

Open source &

active BDD iOS Android HybridFirefox

OSAppiumXcode UI Automation calabash-iOSFrankios-driverKIFRobotiumEspressoselendroid

Test Automation @ Hotwire

• Test Framework = Hotwire BDD• Tools =

Xcode UI testing

• API = Spoofer (mock) vs API calls

Appium – Multi OS Support

And More…

Appium Architecture

Test Script ( Java )

Webdriver (Seleium Java)

Apple Instruments

(Instruments.js)

iPhone / iPad[Simulator /

Device]APP

Android[Simulator /

Device]APP

Android (adb, UIAutomator

(AppiumBootstrap.jar …)

FireFox OS / Hybrid Apps

Firefox OS / Web browser

BDD (Cucumber, Webdriver,

Spring, maven)

CI – Jenkins (Build xcode, start Appium server, mvn install

Appium server (Node.js) JSJSON

BDD – Cucumber & Gherkin

Feature: Hotel Search and Book

Scenario: Search and book hotel as Sign In user Given I'm searching for hotel and attempt to book When I book a hotel as valid signed in user Then I should see confirmation that my hotel is booked

BDD – Cucumber @tags

@iOS @Android @SMOKEFeature: Hotel Search and Book

Scenario: Search and book hotel as Sign In user Given I'm searching for hotel and attempt to book When I book a hotel as valid signed in user Then I should see confirmation that my hotel is booked

BDD – StepDefs//Spring context file@ContextConfiguration("classpath:cucumber.xml")

//hotel object gets input from Spring@Autowired private Hotel hotel;

@Given("^I'm searching for hotel and attempt to book$") public void search() {

hotel.search(); }

Java code

public interface Hotel {void search();}

// iOSpublic class IosHotelImpl implements Hotel {

@OverridePublic void search() {IosSearchScreen iosSearchScreenObj = new IosSearchScreen(getWebDriver());}

// Androidpublic class AndroidHotelImpl implements Hotel {

@OverridePublic void search() {AndroidSearchScreen androidSearchScreenObj = new AndroidSearchScreen(getWebDriver());}

Screen Objects - iOS

// iOS Screen Object

public class iOSSearchScreen extends AbstractScreen {

@FindBy(name = ”Search") private WebElement searchBtn; public void search() { searchBtn.click(); }

}

Screen Objects - Android// Android Screen object

public class AndroidSearchScreen extends AbstractScreen {

@FindBy(id= ”Search") private WebElement searchBtn;

public void search() { searchBtn.click(); }

}

Screen Objectspublic class AbstractScreen {

private final WebDriver webdriver;

public AbstractScreen(WebDriver webdriver) { PageFactory.initElements(webdriver, this); this.webdriver = webdriver; }

protected WebDriver getWebDriver() { return webdriver; }}

BDD – StepDefs//Spring context file@ContextConfiguration("classpath:cucumber.xml")

//hotel object assigned by Spring@Autowired private Hotel hotel;

@Given("^I'm searching for hotel and attempt to book$") public void search() {

hotel.search(); }

Dependency Injection using Spring Framework

Context file - Cucumber.xml

<context:component-scan base-package="com.test" />

<import resource="cucumber-${mobileOS}.xml"/>

Dependency Injection using Spring Framework

Cucumber-ios.xml

<bean id="hotel" class="com.test.ios.hotel.IosHotelImpl"></bean>

<bean id="desiredCapabilities" class="org.openqa.selenium.remote.DesiredCapabilities">

Dependency Injection using Spring Framework

Cucumber-android.xml

<bean id="hotel" class="com.test.android.hotel.AndroidHotelImpl"> </bean> <bean id="desiredCapabilities" class="org.openqa.selenium.remote.DesiredCapabilities">

Managing Dependency

3rd Party Jars

CucumberSpringReportingJunit / testng…

Tests Jars

FeaturesStepDefsiOS-Screen-ObjectsAndroid-Screen-Objects…

Hotwire Apps BDD architecture

Screen Objects (Objects representing screen /

fragment)

Appium server (Node.js)

Apple Instruments

with UI Automation

iPhone / iPad[Simulator /

Device]APP

JSON

JS

Appium locator & tools

WebDriver Locators :• ID• Name• XpathTools :• Appium inspector (iOS)• Uiautomatorviewer (Android)

Appium Sample Code – Send Text and Tap

@FindBy(name = "Email address") private WebElement emailAddress;

@FindBy(name = "Password") private WebElement password;

@FindBy(name = "Sign in") private WebElement signIn; public void enterSignIn(String emailAddress, String password) { this.emailAddress.clear(); this.emailAddress.sendKeys(emailAddress); this.password.clear(); this.password.sendKeys(password); this.signIn.click(); }

Sample Code – Swipe functionalityprivate void swipeFunctionality(WebElement startPoint, WebElement endPoint) {

HashMap<String, Double> args = new HashMap<>(); double x1 = startPoint.getLocation().getX(); double y1 = startPoint.getLocation().getY(); double width1 = startPoint.getSize().getWidth(); double height1 = startPoint.getSize().getHeight(); args.put("touchCount", (double) 1); args.put("startX", (double) (x1 + width1) - 5); args.put("startY", (double) (y1 + height1 / 2));

double x2 = endPoint.getLocation().getX(); double y2 = endPoint.getLocation().getY(); double width2 = endPoint.getSize().getWidth(); double height2 = endPoint.getSize().getHeight(); args.put("touchCount", (double) 1); args.put("endX", (double) (x2 + width2) - 5); args.put("endY", (double) (y2 + height2 / 2));

args.put("duration", (double) 1); executeScript("mobile: swipe", args); }

public Object executeScript(String script, Object... args) { return ((JavascriptExecutor) getAppiumDriver()).executeScript(script, args); }

Sample code (contd.) if (checkInDate.equals(LocalDate.now().plusDays(1))) { LocalDate currentDate = LocalDate.now(); String currentMonth = new SimpleDateFormat("MMMM").format(currentDate.toDateTimeAtStartOfDay().toDate()); String currentDay = currentDate.dayOfMonth().getAsString(); String currentWeekName = currentDate.withDayOfWeek(currentDate.getDayOfWeek()).dayOfWeek().getAsText(); String currentCheckin = "S_Today, " + currentWeekName + ", " + currentMonth + " " + currentDay; WebElement currentElmt = getAppiumDriver().findElement(By.name(currentCheckin)); swipeFunctionality(currentElmt, checkOutElmt); } else if (checkInMonth.equals(checkOutMonth) || nextToCheckInMonth.equalsIgnoreCase(checkOutMonth)) { checkInElmt.click(); if (tapOrDrag.equalsIgnoreCase("tap")) { checkOutElmt.click(); } else if (tapOrDrag.equalsIgnoreCase("tap and drag")) { swipeFunctionality(checkInElmt, checkOutElmt); } else { swipeFunctionality(nextToCheckInElmt, checkOutElmt); } } else { checkInElmt.click(); if (tapOrDrag.equalsIgnoreCase("tap")) { fillCalendarMonth(checkOutMonth); checkOutElmt.click(); } else if (tapOrDrag.equalsIgnoreCase("tap and drag")) { WebElement middleElmt = findMiddleElement(checkInDate, checkInDay, checkInMonth); swipeFunctionality(checkInElmt, middleElmt); fillCalendarMonth(checkOutMonth); swipeFunctionality(middleElmt, checkOutElmt); } else { WebElement middleElmt = findMiddleElement(checkInDate, checkInDay, checkInMonth); swipeFunctionality(nextToCheckInElmt, middleElmt); fillCalendarMonth(checkOutMonth); swipeFunctionality(middleElmt, checkOutElmt); } }

iOS Demo

Native frameworks

• Espresso – Android• Xcode UI testing - iOS

Espresso• Espresso is an open source Android test automation

framework from Google.• Espresso is API for UI Testing on Android.• Espresso core API is small, predictable and easy to learn.• Espresso is a thin layer on top of Android Instrumentation

that makes it easy to write reliable UI tests.• It supports all Android versions.• Tests run optimally fast as there is no need for any sleeps

(tests run on same millisecond when the app becomes idle)

Espresso: Code Examplepublic class EspressoTest extends ActivityInstrumentationTestCase2<HomeScreenActivity> { public EspressoTest() { super(HomeScreenActivity.class); } @Override public void setUp() throws Exception { super.setUp(); getActivity(); }

@Test public void testSearchHotel() { //finding a view with onView and perfoming an action onView(withId(com.hotwire.hotels.R.id.current_location_edittext))

.perform(click()); onView(withId(com.hotwire.hotels.R.id.destination)).perform(typeText("San Franciso\n")); onView(withId(com.hotwire.hotels.R.id.search_hotels_button)).perform(click()); }}

Android Demo

Xcode UI testing

• Available from Xcode 7• New feature added to XCTest framework• UI recording• Ease to locate elements• Run tests on multiple iOS versions• Integrates with Xcode server• Code coverage

But why? We already have Appium

- Faster execution time (1m45sec vs 5m45sec)

- Faster to write too (Record feature)- No Appium/3rd party dependencies- Free code coverage reports- Handles wait times implicitly - Snapshot of failed tests- Consolidated reports

UI testing : Code Exampleimport XCTest

class HotwireUITests: XCTestCase { override func setUp() { super.setUp() continueAfterFailure = false XCUIApplication().launch() }

func testIPadHotel() { //HomeScreen verification let homeScreen = HomeScreen() homeScreen.verifyHomeScreenElements() //Hotel Search with Signed In User homeScreen.navigateToHotel() //Hotel Farefinder verification let hotelFF = HotelFareFinder() hotelFF.verifyFareFinderElements() hotelFF.findHotel() }

import Foundationimport XCTest

class CarBookingScreen { let elementsQuery = XCUIApplication().scrollViews.otherElements let toolbarsQuery = XCUIApplication().toolbars func goToSignIn(){ elementsQuery.buttons["SIGN IN"].tap() } func completeBooking(){ enterCCV() elementsQuery.switches["AgeRestriction"].tap() elementsQuery.buttons["Book Now"].tap() }}

Xcode UI testing for iPad : Demo

Code coverage - iOS

• Tool : LCOV- Collects data from multiple

source files , creates HTML pages containing source code annotated with coverage

• GitHub - XCodeCoverage- Instrument classes- Run tests- Generate reports(./getcov)

Code coverage - iOS

public class Main {

private String s;

public static void main(String[] args){ //some code goes here if (something != null) { //body } }}

public class Main {

private String s; //magic infused

public static void main(String[] args){ //report that method was executed //some code goes here if (something != null) { //report that condition was met //body } }}

Original class Instrumented class

Code coverage – Android using Emma

iOS / Android CIGoalTo provide rapid feedback so that if a bug is introduced into the code base, it can be identified and fixed as soon as possible.

JenkinsWe are using Jenkins for all our CI runs at the moment, triggered on each Git check-in

iOS / Android CI Pipelines• Master Pipeline• Develop Pipeline • Release Pipeline• Feature Pipeline

PollingNodes/hosts from an iOS and Android host-cluster, polled for each leg of the pipeline execution. Artifacts are zipped and transferred between jobs.• Less wait time for resources• Parallel job execution

iOS CIBuild * Unit Tests * iPad Smoke Car/Hotel, iPhone Smoke Car/Hotel * Regression

iOS CI • Reporting & Snapshots

iOS CI

Screenshot testing

• Image comparison – Screen by screen. Why ?– Saves time testing UI layout on various sized

screens– New UI bugs caught faster– Helps reduce Regression Cycle Time

• ImageMagick tool

CI – Screenshot comparison

Analytics automation

• Log analytics / tracking information• Read Appium log file• Assert for expected values

So why are we doing all this?

• Feedback loop – faster @ story level• Regression – 0.5 to 1 day from 5 days• Speed with quality• Visibility

Hotwire automation framework

• To be Open sourced soon. Watch out -> https://github.com/HotwireDotCom/

Questions ?

Recommended