Espresso: How to take a screenshot when a test fails

11 February 2021 stoefln Leave a comment Uncategorized

Ever wondered how to make debugging of tests easier by taking a screenshot right when a test fails? This is what the JUnit TestWatcher can be used for.

public class ScreenshotTestRule extends TestWatcher {

  @Override
  protected void failed(Throwable e, Description description) {
    super.failed(e, description);

    String filename = description.getTestClass().getSimpleName() + "-" + description.getMethodName();

    ScreenCapture capture = Screenshot.capture();
    capture.setName(filename);
    capture.setFormat(CompressFormat.PNG);

    HashSet processors = new HashSet<>();
    processors.add(new BasicScreenCaptureProcessor());

    try {
      capture.process(processors);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Configuring the storage location for the screenshot

BasicScreenCaptureProcessor uses /sdcard/Pictures/ folder and this can lead to IOExceptions on some devices when trying to save the screenshot.

You might get an error similar to this:

java.io.IOException: The directory /storage/emulated/0/Pictures/screenshots does not exist and could not be created or is not writable.

That’s why we override the basic functionality and configure another directory:

package android.support.test.runner.screenshot;

public class CustomScreenCaptureProcessor extends BasicScreenCaptureProcessor {    
  public CustomScreenCaptureProcessor() {
    super(
        new File(
            InstrumentationRegistry.getTargetContext().getExternalFilesDir(DIRECTORY_PICTURES), "test_run_screenshots"
        )
    );
  }
}

Be aware that CustomScreenCaptureProcessor.java needs to be placed in the same directory as your ScreenshotTestRule.java. Of cause, you need to make use of it inside your TestWatcher class:

Instead of

processors.add(new BasicScreenCaptureProcessor());

use

processors.add(new CustomScreenCaptureProcessor());

Timing of taking the screenshot

If you use the rule, as I outlined above, it might take the screenshot too late. For example, your activity might crash and all you get is a screenshot of the home screen.

There is a RuleChain that allows you to get the timing right. This is the code without the RuleChain:

@Rule
public final ActivityTestRule _activityRule = new ActivityTestRule<>(MainActivity.class);

@Rule
public ScreenshotTestWatcher _screenshotWatcher = new ScreenshotTestWatcher();

And this is the code with a RuleChain, to make sure that the screenshot is taken BEFORE the activity is destroyed:

private final ActivityTestRule _activityRule = new ActivityTestRule<>(MainActivity.class);

@Rule
public final TestRule activityAndScreenshotRule = RuleChain
        .outerRule(_activityRule)
        .around(new ScreenshotTestWatcher());

Moving the screenshots to the test automation host:

Probably you want to send the screenshots via email, slack or make them available via a webserver. To do that you can use Android Debug Bridge (ADB):

Simply call adb pull to copy the files over to your host system:

adb pull "/storage/sdcard0/DCIM/test_run_screenshots"

 

Tags: ,

Like this article? there’s more where that came from.