Wednesday, October 26, 2011

Introducing Android WebDriver

[This post is by Dounia Berrada, an engineer on the EngTools team. — Tim Bray]

Selenium WebDriver is a browser automation tool which provides a lightweight and elegant way for testing web apps. Selenium WebDriver is now available as an SDK extra in the Android SDK, and supports 2.3 (Gingerbread) and onwards!

Whether or not your site is optimized for mobile browsers, you can be sure that users will be accessing it from their phones and tablets. WebDriver makes it easy to write automated tests that ensure your site works correctly when viewed from the Android browser. We’ll walk you through some basics about WebDriver and look at it in action.

WebDriver Basics

WebDriver tests are end-to-end tests that exercise the web application just like a real user would. WebDriver models user interactions with a web page such as finger flicks, finger scrolls and long presses. It can rotate the display and interact with HTML5 features such as local storage, session storage and the application cache. Those tests run as part of an Android tests project and are based on Junit. They can be launched from Eclipse or the command line. WebDriver tests can be wired with a continuous integration system and can run on phone and tablet emulators or real devices. Once the test starts, WebDriver opens a WebView configured like the Android browser and runs the tests against it.

WebDriver is an Android SDK extra and can be installed following these instructions. Once you’ve done that you’ll be ready to write tests! There is a comprehensive WebDriver user guide on the Selenium site, but let’s start with a basic example using www.google.com to give you a taste of what’s possible.

Getting Started

First, create an Android project containing an empty activity with no layout.

public class SimpleAppActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

Then create the Android test project that will contain the tests. WebDriver will create the WebView and set the layout automatically in the main Activity.

Let’s write a test that opens the Google home page on Android and issues a query for “weather in San Francisco”. The test will verify that Google returns search results, and that the first result returned is giving the weather in San Francisco.

public class SimpleGoogleTest extends ActivityInstrumentationTestCase2<SimpleAppActivity> {

public void testGoogleShouldWork() {
// Create a WebDriver instance with the activity in which we want the test to run
WebDriver driver = new AndroidDriver(getActivity());
// Let’s open a web page
driver.get("http://www.google.com");

// Lookup for the search box by its name
WebElement searchBox = driver.findElement(By.name("q"));

// Enter a search query and submit
searchBox.sendKeys("weather in san francisco");
searchBox.submit();

// Making sure that Google shows 11 results
WebElement resultSection = driver.findElement(By.id("ires"));
List<WebElement> searchResults = resultSection.findElements(By.tagName("li"));
assertEquals(11, searchResults.size());

// Let’s ensure that the first result shown is the weather widget
WebElement weatherWidget = searchResults.get(0);
assertTrue(weatherWidget.getText().contains("Weather for San Francisco, CA"));
}
}

Now let’s see our test in action! WebDriver will create a WebView with the same configuration as the Android browser in the main UI thread, i.e. the activity thread. The activity will display the WebView on the screen, allowing you to see your web application as the test code is executing.

Interaction Testing

We’ve mentioned that WebDriver supports creating advanced gestures to interact with the device. Let’s use WebDriver to throw an image across the screen by flicking horizontally, and ensure that the next image in the gallery is displayed.

WebElement toFlick = driver.findElement(By.id("image"));
// 400 pixels left at normal speed
Action flick = getBuilder(driver).flick(toFlick, 0, -400, FlickAction.SPEED_NORMAL)
.build();
flick.perform();
WebElement secondImage = driver.findElement(“secondImage”);
assertTrue(secondImage.isDisplayed());

Now, let’s rotate the screen and ensure that the image displayed on screen is resized.

assertEquals(landscapeSize, secondImage.getSize())
((Rotatable) driver).rotate(ScreenOrientation.PORTRAIT);
assertEquals(portraitSize, secondImage.getSize());

What if your test reveals a bug? You can easily take a screenshot for help in future debugging:

File tempFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);

Find Out More

If this has whetted your appetite and you’d like to know more, go ahead and install the Android WebDriver, take a look at the documentation on the Selenium project’s wiki, or just browse the javadocs. For questions and feedback not only of the Android WebDriver but also its desktop brethren, please join webdriver@googlegroups.com. For announcements keep an eye on http://seleniumhq.wordpress.com/.



Tuesday, October 25, 2011

Changes to Library Projects in Android SDK Tools, r14

Last week, we released the SDK for Android 4.0 and a new set of developer tools, now in revision 14. The updated tools include a lot of build changes, many that improve build performance. Also included is an under-the-hood change in how libraries are used by main projects — a first step in improving library support and code reusability. While the change should have little impact on existing projects, some developers have had issues when migrating to the updated tools. Please read below for more information about the change to library projects and how to solve migration issues.

Previously, library projects were handled as extra resource and source code folders to be used when compiling the resources and the application’s source respectively. While this worked fine for most cases, there were two issues.

1. Developers asked us for the ability to distribute a library as a single jar file that included both compiled code and resources. The nature of Android resources, with their compiled IDs prevented this.

2. The implementation of the library projects was extremely fragile in Eclipse. Adding extra source folders outside of the project folders is non-trivial when it needs to be handled automatically, in a way that doesn’t expose a user’s local installation path (which is required for people working in teams through a source control system such as SVN or git).

For r14, we decided to fix both issues at once, by moving to a compiled-code based library mechanism. This solves the implementation fragility in Eclipse and will allow us to, later, enable distribution of libraries as a single jar file.

As you might have seen in the release notes, moving to this new mechanism can affect existing projects in some cases, but there are simple fixes.

The first impact of this change is that the new library project requires the resource IDs generated by libraries to be non final. This prevents the Java compiler from inlining the values in the library code, and therefore prevents usage of the switch statement in the library code. To address such occurrences in your code, Eclipse provides a refactoring action to convert from switch statements to if/else (see here).

Second, some projects may not have been properly migrated to the new mechanism, resulting in projects that fail to compile, with errors such as duplicated classes being added in the dex build step. ADT 14 should have migrated older projects to the new mechanism but the fragility of the old mechanism may have prevented it from happening. This makes projects reference the libraries twice, using both the old and new mechanisms, which then triggers the libraries classes being packaged twice. If you see this in your projects, look in the Package Explorer for extraneous source folders named with the pattern <libraryname>_src. The screenshot to the right shows an example of this.

To fix the project, you must remove the extraneous source folders with the following steps:

  • Right click source folder and choose Build Path > Remove from Build path
  • A dialog will pop up. In it, make sure to check “Also unlink the folder from the project” to completely remove the folder.

With this change to library projects, we pave the way to better support for reusable components. We will continue working to make components easier to create, work with, and manage. Our goal is to make it easy for developers to create apps with great user experiences that easily adapt to all form factors.

Some developers have also told us that they only use library projects internally, that they don’t need to distribute binary versions and would prefer to continue with a source-based mechanism. We are investigating how we could support this alongside the new mechanism.

Finally, I wanted to point out that we are tracking a few known issues (and workaround for them) in the current r14 tools at this page: http://tools.android.com/knownissues.

We are working on a tools update that will include fixes for most of these. We are hoping to have it out shortly.