Posts Tagged ‘Mobile’

Repost: Lessons Learned from Automating iOS Apps – What To Do When Tests Require Camera Roll Resources?

March 18th, 2015 by Bill McGee

This post comes from our friend Jay Sirju at Animoto, who is leveraging Sauce, Appium, and CI methodologies to automate mobile testing for their iOS application.  Check out the original post on their blog.

Here at Animoto, the mobile application development team had spent some time over the past year investigating and implementing CI methodologies into the development cycle of the Animoto Video Maker application for iOS. A major part of this initiative involved creating automated test cases that would run at various times and circumstances.

First a bit of background. When we started this, we had already implemented a good amount of automation for the Animoto website. We had chosen to use Selenium and ran our automated tests against various browsers using Sauce Labs. We decided to extend our existing infrastructure to support running automated tests using theAppium library against Sauce Labs.   For those unfamiliar with mobile testing on Sauce Labs, they use the iOS and Android Simulators to run tests. I know, not ideal, but we can get to that another time.

The Problem

For anyone who has ever launched a fresh iOS Simulator (before Xcode 6), the OS is in it’s factory state. The Animoto Video Maker App transforms your pictures, video, and text, into professional looking videos… see where I’m going?

Screen-Shot-2015-02-18-at-11.31.04-AM

A lot of user flows depend on having some photos in the camera roll. A factory-fresh simulator without any photos means there are man flows we can’t automate. Unfortunately, at the time of writing, Sauce Labs does not have a way to upload assets to populate the Camera Roll, and the simulators are reset after executing each test case. Starting with XCode 6, the Camera Roll does have some images out of the box, but what was really needed were meaningful pictures and videos. So what is needed is a way to populate the Camera Roll while working within the constraints of running tests in Sauce Labs. Well, the app already reads images and pictures from Camera Roll, what about writing to it as well?

Adding or Altering Configuration Profile

Before we get to actually populating the Camera Roll, we need a mechanism to ensure that this logic is performed only when the intent is to run automation. Out of the box, Xcode provides 3 build configurations (Debug, Release, and Distribution). We can edit these configurations, add new ones, or even delete unnecessary ones. In this case, we can simply add a Test configuration to the mix. Once we did that, we were able to change various build settings to help create hooks for app automation. We can start out by adding a Preprocessor Macro for the Test build configuration, so that we can tell the pre-processor when to compile test hooks into the build.

animoto 2

Okay, now we can do some fun stuff with the build. For the sake of brevity, let’s focus specifically on the original issue: Getting pictures and videos into the Camera Roll.

Change Test Configuration Profile Settings

First things first – how does one get pictures and videos up to Sauce Labs? We can simply add them to the iOS project, but that would increase the size of the application bundle regardless of which build configuration is being used. Definitely not ideal. A better choice would be to store them somewhere externally and copy them to the application bundle when the Test configuration is used. This can be done by running a script when building the project.

[code language=”ruby”]if [ ${CONFIGURATION} == "Test" ]; then
cp -r ${PICTURE_AND_VIDEO_LOCATION}/ ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
fi[/code]

Populating the Camera Roll

Now we have an application bundle that contains a bunch of sample pictures and videos. This is great because when the application gets uploaded to Sauce Labs for testing, so do all the sample data.   The following code example assumes all the sample images are in a folder within the application bundle named ‘TestImages’:

[code language=”ruby”]+ (void) populateCameraRoll
{
NSString* absolutePicturePath = [[NSBundle mainBundle] pathForResource:@"TestImages" ofType:nil];
NSArray* pictureList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePicturePath error:nil];

for(int i = 0; i < [pictureList count]; i++)
{
NSString* absolutePictureFilePath =[ NSString stringWithFormat:@"/%@/%@", absolutePicturePath,[pictureList objectAtIndex: i]];

NSData *jpeg = [NSData dataWithContentsOfFile:absolutePictureFilePath];

UIImage *image = [UIImage imageNamed:absolutePictureFilePath];

CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL);
CFDictionaryRef imageMetaDataRef = CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
NSDictionary *imageMetadata = CFBridgingRelease(imageMetaDataRef);
CFRelease(source);

if (image != nil)
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);

[library writeImageToSavedPhotosAlbum:[image CGImage] metadata:imageMetadata completionBlock:^(NSURL *assetURL, NSError *error)
{
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
});
}
}
}
@end[/code]

The above code makes a bunch of assumptions. First, it only works for images. Another folder consisting of sample videos can be created, and the using similar logic and the writeImageToSavedPhotosAlbum method. Next, calling writeImageToSavedPhotosAlbum can indeed fail. For the sake of keeping this example code more readable, error handling was excluded. Retry logic should be included if an error is returned.

Finally, you may have noticed the use of a semaphore in the example code. Writing images to the Camera Roll is actually an asynchronous call, meaning that the call returns immediately while a separate thread processes writing the image data to the Camera Roll. The writeImageToSavedPhotosAlbum method can fail if there are too many threads trying to write image data simultaneously. The semaphore is used ensure that images are written to the Camera Roll sequentially. This makes using the writeImageToSavedPhotosAlbum method much more stable.

Okay, so now that is left is to call the method when running the Test configuration. This can easily be done using the Preprocessor Macro setting that was above mentioned.

[code language=”ruby”]#if TEST
[ANTestClass populateCameraRoll];
#endif[/code]

It is recommended to call the method somewhere deterministic (ie -a button tap). Simply populating the Camera Roll at start-up may mess up launching Apples Instrumentation library because of the Alert displayed when the app accesses the Camera Roll for the first time. This lesson was learned the hard way.

This opens up the ability to add more automated test hooks into the application under test, but as a word of warning; the more hooks added, the more the test configuration of the app diverges from what is being released to customers.

 

Have an idea for a blog post, webinar, or more? We want to hear from you! Submit topic ideas (or questions!) here.

Appium 1.3.6 Available on Sauce

March 4th, 2015 by The Sauce Labs Team

Appium logo w- tagline {final}-01The Appium team is pleased to announce that version 1.3.6 is now available on Sauce.  The new build includes changes from 1.3.5, which was previously unavailable for Sauce users.

iOS
– fix for a bug when driver.get() never returns for page with alert.
– iOS 8.2 support.
– fixed safari startup crashes.
– ensure Appium drops into the right continuation cb when selecting hybrid contexts.

Android
– fix XPath regression where Appium failed to recognize non-ASCII characters.
– fix regression where Appium failed to set ADB’s path during Chromedriver tests.
– now finds the location of adb earlier.
– ensure encoding stream in Bootstrap.jar closes correctly.
– add workaround for issue where UiAUtomator fails to find visible elements.
– fixed undefined member error for the release object.
– add a delete key test.

Selendroid
– upgrade to Selendroid 0.13.0.

Vote To Help Appium Win A DeveloperWeek Award

January 9th, 2015 by Bill McGee

Appium logo w- tagline {final}-01Crowd voting for DeveloperWeek awards is open and will continue through January 15th. Appium was thrown into to the mix as a mobile developer tool of choice, so we’d love your help with voting! Appium is an open source, cross-platform mobile automated testing tool that was developed by Sauce Labs and a thriving community of open source contributors.

Voting is easy. Just follow these four steps:

  1. Register HERE to vote;
  2. Then, visit the Appium nomination page HERE;
  3. Click “Endorse” to vote for Appium;
  4. Share with your network.  Here’s some suggested copy:
    • Twitter: Help  win a award! Click here to vote until 1/15: http://ow.ly/H5fog 
    • LinkedIn: Help Appium, sponsored by Sauce Labs, win a DeveloperWeek award! Click here to learn how to vote until January 15th: http://ow.ly/H5fxJ
    • Google Plus: Help #Appium, sponsored by #SauceLabs, win a #DeveloperWeek award! Click here to learn how to vote until January 15th: http://ow.ly/H5fiu #mobile
    • Facebook: Help #Appium, sponsored by #SauceLabs, win a #DeveloperWeek award! Click here to learn how to vote until January 15th: http://ow.ly/H5ftV #mobile

.

Mobile Testing Talk: What’s New With Appium [RECAP]

December 19th, 2014 by Bill McGee

appium_logo_with_sauce_NEW

Thanks for joining us last week for our latest webinar, Mobile Testing Talk: What’s New With Appium, which featured Jonathan Lipps, the Chief Architect for the Appium Project.

To recap, Jonathan gave listeners a tour of Appium version 1.3.x, including the stability improvements and features the team has added since the Appium 1.0 release back in May of 2014. He also touched on the following:

  •  Appium 1.3.x release features and improvements
  •  New platforms
  •  Better hybrid support
  •  Examples of maturing Appium clients & more

We had great feedback on the content and an influx of interesting questions that got addressed in the Q&A at the end.

Missed the webinar, need to hear it again, or want share it with your team? Check out the slides below (more…)

Appium 1.3.4 Released on Sauce Labs

December 4th, 2014 by Bill McGee

appium_logo_with_sauce_NEW

Appium version 1.3.4 was released on Sauce Labs today. Below is the list of updates, or you can always head over to Github for the latest.  (more…)

Announcing Android 5 Support on Sauce Labs

November 18th, 2014 by Bill McGee

Google-officially-released-Android-5.0-Lollipop-source-code-into-the-AOSP-DetailsWe’ve just released support for emulator testing on Android 5.0 (Lollipop). Android 5 includes some big changes to the look and feel with the move to Material. (more…)

Sharecare Scales Mobile Automated Testing With Sauce Labs [VIDEO]

September 10th, 2014 by Bill McGee

We took a whirlwind trip to New Haven, CT to sit down with Daniel Gempesaw, Software Testing Architect at Sharecare. Sharecare is a wellness platform founded in part by Dr. Oz and Oprah.

We learned that after Sharecare started automating their testing process while using Sauce, time spent for each deploy fell from 7 days to 1 day. With more free time, the team is able to focus on scaling their mobile testing with Appium and employing new best practices such as mobile CI.

Watch this video to learn how they scaled their mobile testing with Sauce Labs.

(more…)

Appium Bootcamp – Chapter 5: Writing and Refactoring Your Tests

August 5th, 2014 by Bill McGee

appium_logoThis is the fifth post in a series called Appium Bootcamp by noted Selenium expert Dave Haeffner

Read:  Chapter 1 Chapter 2 | Chapter 3 | Chapter 4 | Chapter 5 | Chapter 6 | Chapter 7 | Chapter 8

Dave recently immersed himself in the open source Appium project and collaborated with leading Appium contributor Matthew Edwards to bring us this material. Appium Bootcamp is for those who are brand new to mobile test automation with Appium. No familiarity with Selenium is required, although it may be useful. This is the fifth of eight posts; two new posts will be released each week.

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.

Quick 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:

# filename: Gemfile

source 'https://rubygems.org'

gem 'rspec', '~> 3.0.0'
gem 'appium_lib', '~> 4.0.0'
gem 'appium_console', '~> 1.0.1'

After creating the Gemfile run 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.

Capabilities

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.

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

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.rband place it in the ios folder.

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

Now let’s write our test to launch Appium for iOS and perform a simple navigation test.

In RSpec, 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 iOSappium.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 driver_quit in 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 Launch) with 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.

You can learn more about page objects here and here.

Refactoring Your First Test

Let’s create a new directory called pages within our ios directory and create two new files in it: home.rb and 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.

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

Let’s open up ios/pages/home.rb to create our first page object.

 

# filename: ios/pages/home.rb

module Pages
  module Home
    class << self

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

      def title
        @found_cell.name.split(',').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 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.

In Home, we’ve created some simple static methods to mimic the behavior of the home screen (e.g., first_celltitleclick). 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.

# filename: pages/inner_screen.rb

module Pages
  module InnerScreen
    class << self

      def has_text(text)
        wait { text_exact text }
      end

    end
  end
end

module Kernel
  def inner_screen
    Pages::InnerScreen
  end
end

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.

# filename: ios/spec/navigation_spec.rb

require 'appium_lib'
require_relative '../pages/home'
require_relative '../pages/inner_screen'

describe 'Home Screen Navigation' do

  before(:each) do
    appium_txt = File.join(Dir.pwd, 'appium.txt')
    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 'First cell' do
    cell_title = home.first_cell.title
    home.first_cell.click
    inner_screen.has_text cell_title
  end

end

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.

Central Config

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.

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

Let’s open up common/spec_helper.rb, add our test setup to it, and polish it up.

 

# filename: common/spec_helper.rb

require 'rspec'
require 'appium_lib'

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

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

setup_driver
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’ve created a couple of methods that get executed when the file is loaded. One is to setup (but not start) Appium and another is to promote the methods to our page objects and tests. This approach is taken to make sure that only one instance of Appium is loaded at any one time.

We then configure our test actions so they run before and after each test. In them we are starting an Appium session and then ending it.

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

# filename: ios/spec/navigation_spec.rb

require_relative '../pages/home'
require_relative '../pages/inner_screen'
require_relative '../../common/spec_helper'

describe 'Home Screen Navigation' do

  it 'First cell' do
    cell_title = home.first_cell.title
    home.first_cell.click
    inner_screen.has_text cell_title
  end

end

Note the order of the require_relative statements — they are important. We need to load our page objects before we can load our spec_helper, or else the test won’t run.

If we run the tests from within the ios directory with rspec, we can see everything execute just like it did before.

Now that we have iOS covered, let’s wire up an Android test, some page objects, and make sure our test code to supports both devices.

Including Android

It’s worth noting that in your real world apps you may be able to have a single set of tests and segmented page objects to help make things run seamlessly behind the scenes for both devices. And while the behavior in our Android test app is similar to our iOS test app, it’s design is different enough that we’ll need to create a separate test and page objects.

Let’s start by creating spec and pages folders within the android directory and then creating page objects in pages (e.g., home.rb and inner_screen.rb) and a test file in spec (e.g., navigation_spec.rb).

├── Gemfile
├── Gemfile.lock
├── android
│   ├── appium.txt
│   ├── pages
│   │   ├── 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’s open and populate our page objects and test file.

module Pages
  module Home
    class << self

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

      def click
        @found_cell.click
      end

    end
  end
end

module Kernel
  def home
    Pages::Home
  end
end

This page object is similar to the iOS one except there’s no title search (since we won’t be needing it).

module Pages
  module InnerScreen
    class << self

      def has_text(text)
        wait { find_exact text }
      end

    end
  end
end

module Kernel
  def inner_screen
    Pages::InnerScreen
  end
end

In this page object we’re performing a search for an element by text (similar to the iOS example), but using find_exact instead of text_exact because of how the app is designed (we need to perform a broader search that will search across multiple attributes, not just the text attribute).

Now let’s wire up our test.

require_relative '../pages/home'
require_relative '../pages/inner_screen'
require_relative '../../common/spec_helper'

describe 'Home Screen Navigation' do

  it 'First cell' do
    home.first_cell.click
    inner_screen.has_text 'Accessibility Node Provider'
  end

end

Now if we cd into the android directory and run our test with rspec it should launch the Android emulator, load the app, click the first cell, and then end the session. The emulator will remain open, but that’s something we’ll address in a future post.

One More Thing

If we use the console with the code that we have right now, we won’t be able to reference the page objects we’ve created — which will be a bit of a pain if we want to reference them when debugging test failures. Let’s fix that.

Let’s create a new file in our android/spec and ios/spec directories called requires.rb. We’ll move our require statements out of our test 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’s what one of them should look like:

# filename: ios/spec/requires.rb

# require the ios pages
require_relative '../pages/home'
require_relative '../pages/inner_screen'

# setup rspec
require_relative '../../common/spec_helper'

Next, we’ll want to update our tests to use this file.

require_relative 'requires'

describe 'Home Screen Navigation' do

  it 'First cell' 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 'requires'

describe 'Home Screen Navigation' do

  it 'First cell' do
    home.first_cell.click
    inner_screen.has_text 'Accessibility Node Provider'
  end

end

Now that we have a central requires.rb for each device, we can tell the Appium Console to use it. To do that, we’ll need to add some additional info to our appium.txt files.

 

# filename: ios/appium.txt

[caps]
deviceName = "iPhone Simulator"
platformName = "ios"
app = "../../../apps/UICatalog.app.zip"

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

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

[appium_lib]
require = ["./spec/requires.rb"]

This new require value is only used by the Appium Console. Now if we run arc from either the ios or android directories, we’ll be able to access the page objects just like in our tests.

And if we run our tests from either directory, they will still work as directed.

Outro

Now that we have our tests, page objects, and central configuration all sorted, it’s time to look at wrapping our test execution and make it so we can run our tests in the cloud.

Read:  Chapter 1 Chapter 2 | Chapter 3 | Chapter 4 | Chapter 5 | Chapter 6 | Chapter 7 | Chapter 8

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

Follow Dave on Twitter – @tourdedave

Appium Bootcamp – Chapter 4: Your First Test

July 31st, 2014 by Bill McGee

appium_logoThis is the fourth post in a series called Appium Bootcamp by noted Selenium expert Dave Haeffner

Read:  Chapter 1 Chapter 2 | Chapter 3 | Chapter 4 | Chapter 5 | Chapter 6 | Chapter 7 | Chapter 8

Dave recently immersed himself in the open source Appium project and collaborated with leading Appium contributor Matthew Edwards to bring us this material. Appium Bootcamp is for those who are brand new to mobile test automation with Appium. No familiarity with Selenium is required, although it may be useful. This is the fourth of eight posts; two new posts will be released each week.

There are a good deal of similarities between Selenium and Appium tests. We will be using similar actions (like click) along with some kind of wait mechanism (e.g., an explicit wait) to make our tests more resilient. There will also be an assertion used to determine if our actions were successful or not.

In order to put these concepts to work, let’s consider the basic structure of the test apps we’ve been working with. They are straightforward in that they both have text elements that, when clicked, take you to a dedicated page for that element (e.g., Accessibility triggers the Accessibility page). Let’s step through our first set of test actions (in the console) that we’ll use to automate this behavior; verifying that each element brings us to the correct page.

Let’s dig in with some examples.

An iOS Example

The behavior of our app can be easily mapped to test actions by first using a text match to find the element we want, and clicking it. We can then make sure we are in the right place by performing another text match (this time an exact text match). When we wire this up to our test framework, this match will be responsible for passing or failing the test. More on that in the next post.

text('Buttons, Various uses of UIButton').click
text_exact 'Buttons'

The only problem with this approach is that it is not resilient. The global wait for each test action (a.k.a. an implicit wait) is set to 0 seconds by default. So if there is any delay in the app, the test action will not complete and throw an element not found exception instead.

To overcome these timing problems we can employ an explicit wait around our test actions (both the click and the exact text match). This is simple enough to do with the wait command.

wait { text('Buttons, Various uses of UIButton').click }
wait { text_exact 'Buttons' }

These test actions are resilient now, but they’re inflexible since we were using statically coded values. Let’s fix that by using dynamic values instead.

cell_1 = wait { text 2 }
cell_title = cell_1.name.split(',').first

wait { cell_1.click }
wait { text_exact cell_title }

Now we’re finding the first text by it’s index. Index 2 contains the first element (a.k.a. a cell), whereas index 1 is the table header. After that, we’re extracting the name and dynamically finding the title. Now our test will continue to work if there are any text changes.

This is good, but now let’s expand things to cover the rest of the app.

cell_names = tags('UIATableCell').map { |cell| cell.name }

cell_names.each do |name|
  wait { text_exact(name).click }
  wait { text_exact name.split(',').first }
  wait { back }
end

We first grab the names of each clickable cell, storing them in a collection. We then iterate through the collection, finding each element by name, clicking it, performing an exact match on the resulting page, and then going back to the main screen. This is repeated until each cell is verified.

This works for cells that are off the screen (e.g., out of view) since Appium will scroll them into view before taking an action against them.

An Android Example

Things are pretty similar to the iOS example. We perform a text match, click action, and exact text match.

text('Accessibility').click
text_exact 'Accessibility Node Provider'

We then make things resilient by wrapping them in an explicit wait.

wait { text('Accessibility').click }
wait { text_exact 'Accessibility Node Provider' }

We then make our selection more flexible by upgrading to dynamic values.

cell_1 = wait { text 2 }

wait { cell_1.click }
wait { find_exact 'Accessibility Node Provider' }

We then expand things to exercise the whole app by collecting all of the clickable elements and iterating through them.

cell_names = tags('android.widget.TextView').map { |cell| cell.name }

cell_names[1..-1].each do |cell_name|
  wait { scroll_to_exact(cell_name).click }
  wait_true { ! exists { find_exact cell_name } }
  wait { back }
  wait { find_exact('Accessibility'); find_exact('Animation')  }
end

A few things to note.

The first item in the cell_names collection is a header. To discard it, we use cell_name[1..-1] which basically says start with the second item in the collection (e.g., [1) and continue (e.g., ..) all the way until the end (e.g.,-1]).

In order to interact with cells that are off the screen, we will need to use the scroll_to_exact command, and perform a click against that (instead of a text match).

Since each sub-screen doesn’t have many unique attributes for us to verify against, we can at the very least verify that we’re no longer on the home screen. After that, we verify that we are brought back to the home screen.

Outro

Now that we have our test actions sussed out, we’re ready to commit them to code and plug them into a test runner.

Read:  Chapter 1 Chapter 2 | Chapter 3 | Chapter 4 | Chapter 5 | Chapter 6 | Chapter 7 | Chapter 8

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

Follow Dave on Twitter – @tourdedave

 

Sign Up for the First-Ever Appium Roadshow on August 20th in New York City

July 30th, 2014 by Bill McGee

appium_logoWe don’t know if you heard, but mobile is kind of a big deal.

Naturally, Appium – the only open source, cross-platform test automation tool for native, hybrid, and mobile web apps – emerged out of the need to Test All The (Mobile) Things.  Last May, battle-tested Appium 1.0 was released, and now this Appium show is hitting the road!

Details and ticket links below. Hope to see you in New York!

*****

Sign Up for the First-Ever Appium Roadshow on August 20th

Appium Roadshow – NYC is a two part, day-long event held on Wednesday, August 20 at Projective Space – LES in Manhattan’s Lower East Side.

Part 1 – Appium in the Wild

8:30 AM – 1:00 PM – Free

The morning session will showcase presentations from Gilt Groupe, Sharecare, Softcyrlic, and Sauce Labs. Topics will cover real-world examples, lessons learned, and best practices in mobile app test automation using Appium. Featured speakers include:

  • Matthew Edwards – Mobile Automation Lead, Aquent
  • Daniel Gempesaw – Software Testing Architect, Sharecare
  • Matt Isaacs – Engineer, Gilt Groupe
  • Jonathan Lipps – Director of Ecosystem and Integrations, Sauce Labs
  • Sundar Sritharan – Delivery Manager, Softcrylic

This event is free. Breakfast and lunch included. Reserve your seat now – register here.

Part 2 – Appium Workshop

1:30 PM – 5:30 PM – $100

Matthew Edwards, a leading contributor to the Appium project, will provide a hands-on workshop to help you kick start your Appium tests.  He’ll talk you through how to set up the environment needed for native iOS and Android automation with Ruby.  You’ll then download and configure the Appium.app to enable test writing. Then, Matthew will demonstrate how to kick up an Appium server and then run a test.

This event is limited to just 40 participants. Reserve your seat now – register here.