Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Spoon to take screenshots for Espresso tests?

I've been trying to follow the instructions on getting Spoon 1.1.14 to take screenshots for failing Espresso tests.

What's the best way to configure this with a custom Espresso FailureHandler?

like image 725
Dan J Avatar asked Apr 28 '15 20:04

Dan J


People also ask

How do I take a screenshot in BrowserStack?

You can take screenshots of your Selenium test in the following two ways: Using the Visual Logs capability, BrowserStack Automate can automatically take screenshots during the test run. Explicitly from your test script as and when required and download the screenshot to your local machine.

What is espresso testing used for?

Espresso tests state expectations, interactions, and assertions clearly without the distraction of boilerplate content, custom infrastructure, or messy implementation details getting in the way. Espresso tests run optimally fast!


1 Answers

Here's how I am doing this at the moment:

public class MainScreenTest extends BaseStatelessBlackBoxEspressoTest<LaunchActivity> {

    public MainScreenTest() {
        super(LaunchActivity.class);
    }

    public void testMainScreen() {
        // Unfortunately this must be explicitly called in each test :-(
        setUpFailureHandler();

        onView(withId(R.id.main_circle)).
                check(matches(isDisplayed()));
    }
}

My base Espresso test class sets up the custom FailureHandler (I like using a base class to hold lots of other common code):

public abstract class BaseStatelessBlackBoxEspressoTest<T extends Activity> extends BaseBlackBoxTest<T> {

    public BaseStatelessBlackBoxEspressoTest(Class clazz) {
        super(clazz);
    }

    @Before
    public void setUp() throws Exception {
        super.setUp();
        getActivity();
    }

    public void setUpFailureHandler() {
        // Get the test class and method.  These have to match those of the test
        // being run, otherwise the screenshot will not be displayed in the Spoon 
        // HTML output.  We cannot call this code directly in setUp, because at 
        // that point the current test method is not yet in the stack.
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        String testClass = trace[3].getClassName();
        String testMethod = trace[3].getMethodName();

        Espresso.setFailureHandler(new CustomFailureHandler(
                getInstrumentation().getTargetContext(),
                testClass,
                testMethod));
    }

    private static class CustomFailureHandler implements FailureHandler {
        private final FailureHandler mDelegate;
        private String mClassName;
        private String mMethodName;

        public CustomFailureHandler(Context targetContext, String className, String methodName) {
            mDelegate = new DefaultFailureHandler(targetContext);
            mClassName = className;
            mMethodName = methodName;
        }

        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher) {
            try {
                mDelegate.handle(error, viewMatcher);
            } catch (Exception e) {
                SpoonScreenshotAction.perform("espresso_assertion_failed", mClassName, mMethodName);
                throw e;
            }
        }
    }
}

...and here is the slightly modified screenshot code from the Gist posted by Square:

/**
 * Source: https://github.com/square/spoon/issues/214#issuecomment-81979248
 */
public final class SpoonScreenshotAction implements ViewAction {
    private final String tag;
    private final String testClass;
    private final String testMethod;

    public SpoonScreenshotAction(String tag, String testClass, String testMethod) {
        this.tag = tag;
        this.testClass = testClass;
        this.testMethod = testMethod;
    }

    @Override
    public Matcher<View> getConstraints() {
        return Matchers.anything();
    }

    @Override
    public String getDescription() {
        return "Taking a screenshot using spoon.";
    }

    @Override
    public void perform(UiController uiController, View view) {
        Spoon.screenshot(getActivity(view), tag, testClass, testMethod);
    }

    private static Activity getActivity(View view) {
        Context context = view.getContext();
        while (!(context instanceof Activity)) {
            if (context instanceof ContextWrapper) {
                context = ((ContextWrapper) context).getBaseContext();
            } else {
                throw new IllegalStateException("Got a context of class "
                        + context.getClass()
                        + " and I don't know how to get the Activity from it");
            }
        }
        return (Activity) context;
    }    

    public static void perform(String tag, String className, String methodName) {
        onView(isRoot()).perform(new SpoonScreenshotAction(tag, className, methodName));
    }
}

I'd love to find a way to avoid calling setUpFailureHandler() in every test - please let me know if you have a good idea on how to avoid this!

like image 114
Dan J Avatar answered Sep 26 '22 01:09

Dan J