Automating app store screenshots

If there is one part of developing an Android app that I find tedious it has to be updating the screenshots for the store listing after a new release. The drudgery of deciding how I want the app to look for each screenshot, manually preparing it with data and then taking the screenshots, has finally driven me to automate the process.

Technologies

My Android app is already making use of the following frameworks, so it made sense to reuse them.

  • AndroidJUnitRunner - Runs the instrumentation tests.
  • UI Automator - In this context it is only used for taking screenshots.
  • Espresso - Controls the app during UI tests; reliably clicking buttons and typing text etc.
  • GreenCoffee - Runs acceptance tests written in Gherkin using the AndroidJUnitRunner.

Preparing the app for screenshots

The following is an example Gherkin (Given-When-Then) scenario that I use for taking the screenshots. The end result is the app configured, rotated and fed with dummy data via a mocked sensor, ready for the last step to take a screenshot…

Given I have turned on 'Detailed Readings' in the Preferences
And the device has been rotated
When the sensor provides random values with 150ms intervals aligned to:
  | Values | X | Y | Z |
  | 20     | 5 | 3 | 1 |
  | 6      | 8 | 3 | 1 |
  | 50     | 5 | 3 | 1 |
Then a screenshot named 'orientation_1_detailed_spike.png' is saved in the directory 'test-screenshots'
Animation of the app following the scenario's steps

Taking the screenshots

The most pertinent step above is the last, which takes the screenshot of the app. This is performed using UI Automator’s convenient takeScreenshot method, and is encapsulated as the following Step…

public class ScreenshotSteps extends GreenCoffeeSteps {

    private static final String TAG = "ScreenshotSteps";

    private final UiDevice uiDevice;

    public DeviceSteps(final UiDevice uiDevice) {
        this.uiDevice = uiDevice;
    }

    @Then("^a screenshot named '(.*)' is saved in the directory '(.*)'$")
    public void a_screenshot_named_x_is_saved_in_the_directory_x(final String filename, final String directory) throws Exception {
        final File screenshotDir = new File(
            Environment.getExternalStorageDirectory().getPath(),
            directory
        );

        if(!screenshotDir.exists() && !screenshotDir.mkdirs()) {
            throw new IOException("Failed to create directory: " + screenshotDir);
        }

        final File screenshotFile = new File(screenshotDir, filename);
        Log.i(TAG, String.format("Saving screenshot: %s", screenshotFile));

        if (!uiDevice.takeScreenshot(screenshotFile)) {
            throw new IOException(String.format(
                "Failed to save screenshot: %s", screenshotFile
            ));
        }
    }
}

Transferring the screenshots

If using a service like AWS Device Farm the screenshots can be automatically saved and presented after each run. Such a service is great if you want screenshots from any of the 200+ Android devices they have available.

Locally though, when run from an Android Virtual Device, the screenshots can be transferred over with ADB’s pull command.

$ adb pull /storage/sdcard/test-screenshots/ ~/Desktop/

Conclusion

Since automating the taking of screenshots I’ve integrated it into the app’s continuous delivery pipeline which means when a new feature is merged I am presented with screenshots like the following; and given I’m using AWS Device Farm the screenshots are taken on the most popular devices for my app.

Two screenshots of my app