Skip to main content

Capture screenshots

As part of debugging, capturing screenshots makes it easier to identify the exact step in your test where the failure occurred. These screenshots also help in identifying any layout or design-related issues in your application.

You can capture screenshots using either of the following two ways:

  • Native screenshot feature: A native screenshot feature in App Automate to capture screenshots. This feature is supported with all Android versions.
  • Spoon library: The screenshot feature provided as part of the Spoon library. This feature is supported only with Android 10 and lower versions.

Capture screenshots of your test execution

Use the information in the following tabs to edit your scripts and capture screenshots.

1. Add the native screenshots file to your project

Note: Check out our Sample Espresso project that uses the native screenshot feature to capture screenshots of test execution.

Create a file named NativeScreenshot.java in your project directory where your test files are located and add the following code to this file:

NativeScreenshot.java
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Environment;

import androidx.test.runner.screenshot.BasicScreenCaptureProcessor;
import androidx.test.runner.screenshot.ScreenCapture;
import androidx.test.runner.screenshot.Screenshot;

import java.io.File;
import java.io.IOException;
import java.util.regex.Pattern;

public final class NativeScreenshot {

    private static String methodName;
    private static String className;
    private static final Pattern SCREENSHOT_NAME_VALIDATION = Pattern.compile("[a-zA-Z0-9_-]+");

    private NativeScreenshot() {}

    /**
     * Captures screenshot using Androidx Screenshot library and stores in the filesystem.
     * Special Cases:
     * If the screenshotName contains spaces or does not pass validation, the corresponding
     * screenshot is not visible on BrowserStack's Dashboard.
     * If there is any runtime exception while capturing screenshot, the method throws
     * Exception and the test might fail if exception is not handled properly.
     * @param screenshotName    a screenshot identifier
     * @return path to the screenshot file
     */
    public static String capture(String screenshotName) {
        StackTraceElement testClass = findTestClassTraceElement(Thread.currentThread().getStackTrace());
        className = testClass.getClassName().replaceAll("[^A-Za-z0-9._-]", "_");
        methodName = testClass.getMethodName();
        EspressoScreenCaptureProcessor screenCaptureProcessor = new EspressoScreenCaptureProcessor();

        if (!SCREENSHOT_NAME_VALIDATION.matcher(screenshotName).matches()) {
            throw new IllegalArgumentException("ScreenshotName must match " + SCREENSHOT_NAME_VALIDATION.pattern() + ".");
        } else {
            ScreenCapture capture = Screenshot.capture();
            capture.setFormat(Bitmap.CompressFormat.PNG);
            capture.setName(screenshotName);

            try {
                return screenCaptureProcessor.process(capture);
            } catch (IOException e) {
                throw new RuntimeException("Unable to capture screenshot.", e);
            }
        }
    }

    /**
     * Extracts the currently executing test's trace element based on the test runner
     * or any framework being used.
     * @param trace stacktrace of the currently running test
     * @return StackTrace Element corresponding to the current test being executed.
     */
    private static StackTraceElement findTestClassTraceElement(StackTraceElement[] trace) {
        for(int i = trace.length - 1; i >= 0; --i) {
            StackTraceElement element = trace[i];
            if ("android.test.InstrumentationTestCase".equals(element.getClassName()) && "runMethod".equals(element.getMethodName())) {
                return extractStackElement(trace, i);
            }

            if ("org.junit.runners.model.FrameworkMethod$1".equals(element.getClassName()) && "runReflectiveCall".equals(element.getMethodName())) {
                return extractStackElement(trace, i);
            }

            if ("cucumber.runtime.model.CucumberFeature".equals(element.getClassName()) && "run".equals(element.getMethodName())) {
                return extractStackElement(trace, i);
            }
        }

        throw new IllegalArgumentException("Could not find test class!");
    }

    /**
     * Based on the test runner or framework being used, extracts the exact traceElement.
     * @param trace stacktrace of the currently running test
     * @param i a reference index
     * @return trace element based on the index passed
     */
    private static StackTraceElement extractStackElement(StackTraceElement[] trace, int i) {
        int testClassTraceIndex = Build.VERSION.SDK_INT >= 23 ? i - 2 : i - 3;
        return trace[testClassTraceIndex];
    }

    private static class EspressoScreenCaptureProcessor extends BasicScreenCaptureProcessor {
        private static final String SCREENSHOT = "screenshots";

        EspressoScreenCaptureProcessor() {
            File screenshotDir = new File(String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)), SCREENSHOT);
            File classDir = new File(screenshotDir, className);
            mDefaultScreenshotPath = new File(classDir, methodName);
        }

        /**
         * Converts the filename to a standard path to be stored on device.
         * Example: "post_addition" converts to "1648038895211_post_addition"
         * which is later suffixed by the file extension i.e. png.
         * @param filename  a screenshot identifier
         * @return custom filename format
         */
        @Override
        protected String getFilename(String filename) {
            return System.currentTimeMillis() + "_" + filename;
        }
    }
}

2. Edit your test scripts to take screenshots

Call the NativeScreenshot.capture("tag") method to capture screenshots from your Espresso tests, as shown in the following sample code snippet.

The tag parameter is used to provide a name to the screenshot on your App Automate’s session detail page.

@Test
  public void ensureAdditionWorks() {
    // test actions
    onView(withId(R.id.buttonOne)).perform(click());
    onView(withId(R.id.buttonTwo)).perform(click());
    onView(withId(R.id.buttonAdd)).perform(click());
    // other test actions ...
    onView(withId(R.id.editText)).check(matches(withText("33")));

    // capture the screenshot and add the 'post_addition' tag to the screenshot
    NativeScreenshot.capture("post_addition");
  }

3. Enable screenshots on BrowserStack

Add the debugScreenshots parameter in the REST API request to enable screenshots on your Espresso test session.

The following sample cURL command shows the parameters used to enable screenshots on Android v7.1:

curl -u "YOUR_USERNAME:YOUR_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" \
-d '{"debugScreenshots": true, "devices": ["Google Pixel-7.1"], "app": "<app_id>", "testSuite": "<test_suite_id>"}' \
-H "Content-Type: application/json"

The following table lists the parameters used in the cURL request for enabling screenshots:

Parameter Description Values
debugScreenshots Capture screenshots during Espresso test execution true, false
Default: false
devices Name and OS version of the device to run the test on For example, Google Pixel-7.1.
Check out the list of devices available in App Automate.
app ID of the application you receive when you upload your application on App Automate For example, bs://3205774d1560ee3f5acdf
testSuite ID of the test suite you receive when you upload your test suite on App Automate For example, bs://4205214d1560we3f5acdfe

4. View screenshots of the test execution

Navigate to your App Automate dashboard. On your session detail page, click the Screenshots tab. You can view screenshots for each test case as shown in the following image:

View screenshots for your test execution on dashboard

The name of the screenshot is set to the tag value that you provided inside NativeScreenshot.capture("tag") method.

1. Add required dependencies to your project

Follow the instructions mentioned on the Spoon project’s page to add required dependencies to your project.

2. Change storage permissions

Add the WRITE_EXTERNAL_STORAGE permissions in your app’s AndroidManifest.xml file to save screenshots, as shown in the following sample code snippet:

AndroidManifest.xml
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
</manifest>
Note: If you’re targeting your app on Android v10, add the android:requestLegacyExternalStorage="true" tag in the Application tag of the AndroidManifest.xml file.

3. Edit your test scripts to take screenshots

Call the Spoon.screenshot(activityName, tag) method to capture screenshots from your Espresso tests, as shown in the following sample code snippet.

The tag parameter is used to provide a name to the screenshot on your App Automate’s session detail page.

@Test
public void ensureAdditionWorks() {
    onView(withId(R.id.buttonOne)).perform(click());
    onView(withId(R.id.buttonTwo)).perform(click());
    onView(withId(R.id.buttonAdd)).perform(click());
    onView(withId(R.id.editText)).check(matches(withText("33")));

    // capture the screenshot and add the 'post_addition' tag to the screenshot
    Spoon.screenshot(mainActivity, "post_addition");
}

4. Enable screenshots on BrowserStack

Add the debugScreenshots parameter in the REST API request to enable screenshots on your Espresso test session.

The following sample cURL command shows the parameters used to enable screenshots on Android v7.1:

curl -u "YOUR_USERNAME:YOUR_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/espresso/v2/build" \
-d '{"debugScreenshots": true, "devices": ["Google Pixel-7.1"], "app": "<app_id>", "testSuite": "<test_suite_id>"}' \
-H "Content-Type: application/json"

The following table lists the parameters used in the cURL request for enabling screenshots:

Parameter Description Values
debugScreenshots Capture screenshots during Espresso test execution true, false
Default: false
devices Name and OS version of the device to run the test on For example, Google Pixel-7.1.
Check out the list of devices available in App Automate
app ID of the application you receive when you upload your application on App Automate For example, bs://3205774d1560ee3f5acdf
testSuite ID of the test suite you receive when you upload your test suite on App Automate For example, bs://4205214d1560we3f5acdfe

5. View screenshots of the test execution

Navigate to your App Automate dashboard. On your session detail page, click the Screenshots tab. You can view screenshots for each test case as shown in the following image:

View screenshots for your test execution on dashboard

The name of the screenshot is the tag value set in your script when you used the Spoon.screenshot(activityName, tag) method.

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked






Thank you for your valuable feedback

Is this page helping you?

Yes
No

We're sorry to hear that. Please share your feedback so we can do better

Contact our Support team for immediate help while we work on improving our docs.

We're continuously improving our docs. We'd love to know what you liked






Thank you for your valuable feedback!

Talk to an Expert
Talk to an Expert