I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 635 posts at DZone. You can read more from them at their website. View Full User Profile

Selenium on Android

04.17.2012
| 8052 views |
  • submit to reddit

Testing web applications is not only based on unit and functional tests for the server-side (PHP, Java) and client-side (JavaScript) components, but also on end-to-end tests like the ones performed with Selenium. Selenium is capable of driving a real browser like Firefox in the same way a user would do, letting you express a test with a series of page to load, element selections, clicks and typed characters.

Selenium 2 exposes a WebDriver API that can be implemented by many different servers: not only desktop browsers can be driven with it, but also browsers on mobile devices like the standard Android browser.

In this tutorial, we'll experiment with the Android Driver and set up a couple of tests to run inside an Android virtual device.

Android setup

The best choice for running tests is an emulator, as it is more versatile than a real mobile terminal and can be easily upgraded through various versions (while for example I do not have a real Android 4.x device, the highly recommended version.)

If you have already a virtual device setup, you can skip this step. If not, before running the emulator make sure you have installed all the required packages listed here along with the Android SDK.

You should have the commands android, emulator, and adb available on your path to continue.

$ android create avd -n my_android -t 12 -c 100M

will create a virtual device named my_android. To get a list of possible targets for the -t option, execute:

$ android list targets

You can now run the emulator with:

$ emulator -avd my_android

With android avd you can open the configuration window, and add the Gpu acceleration directive as a new hardware option (with the value yes). I didn't see any difference in the tests after adding the option, though.

Installation

$ adb devices
List of devices attached
emulator-5554   device

will list the available devices: you should wait until `device` is displayed instead of `offline`.

$ adb -s emulator-5554 -e install -r android-server.apk

will install the WebDriver application, which can be downloaded from Selenium's website.

Start the Android Driver with:

$ adb -s emulator-5554 shell am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity

and wait a couple of seconds for it to return the control to the shell.

Since the emulator does not have an IP address, we have to forward the host 4000 port to the device's 8080 (4000 is just an example) with:

adb -s emulator-5554 forward tcp:4000 tcp:8080

In case of any error, display the logs with:

adb logcat

Writing a test

Tests that want to run inside Android will use the Json Wire Protocol, that accepts requests from bindings written in any language. In fact, the tests will interface with localhost:4000, the REST-like API of WebDriver, which is in turn forwarded to the emulator's 8080 port.

The browser to request is 'android', of course. Even from Java code, which usually interfaces with WebDriver directly, a RemoteWebDriver class is available.

Example

In this example, I also setup a tiny web server on 192.168.0.2 (the local machine) to serve some HTML pages that the tests exercise. In a real scenario, you will serve your web application instead.

Since I target the application web server with its IP address, this test can work also with real phones that connect to the wireless local network.

<?php
class AndroidTest extends PHPUnit_Extensions_Selenium2TestCase
{
    public function setUp()
    {
        $this->setHost('localhost');
        $this->setPort(4000);
        $this->setBrowser('android');
        $this->setBrowserUrl('http://192.168.0.2:8080');
    }
    public function testLoadsAnHtmlPage()
    {
        $this->url('html/test_open.html');
        $this->assertEquals('Test open', $this->title());
    }

    public function testTypingViaTheKeyboard()
    {
        $this->url('html/test_type_page1.html');
        $usernameInput = $this->byName('username');
        $usernameInput->value('giorgio');
        $this->assertEquals('giorgio', $usernameInput->value());

        $passwordInput = $this->byName('password');
        $passwordInput->value('password');
        $this->assertEquals('password', $passwordInput->value());

        $this->clickOnElement('submitButton');
        $h2 = $this->byCssSelector('h2');
        $this->assertRegExp('/Welcome, giorgio!/', $h2->text());
    }
}

This is the result of the tests (the HTML pages are taken from PHPUnit_Selenium's test suite):

$ phpunit --colors AndroidTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

..

Time: 02:22, Memory: 3.75Mb

OK (2 tests, 4 assertions)

Conclusions

These tests are slow, and we didn't perform any optimization. Not only running a browser through WebDriver is slower than executing unit tests, but we are also inside an emulator that translates instructions meant for another family of processors. These ordinary Selenium tests would take only seconds to run in a desktop browser.
As for all Selenium tests, you can actually see (if you keep the WebDriver app in the foreground) the tests being executed in an instance of the android browser. However, all commands are available from the shell and the automation of the emulator execution and the test run is only one step away.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)