Is it possible to disable Toasts or wait until toast disappears while testing
Asked Answered
S

2

9

I am testing an app with Espresso. I have one question is it possible to wait until there are no toast are currently being showed. I have a lot of different toast in my app, but while testing I have problems with them because as far as I can guess focus is gone to the toast and I am getting quite another view hierarchy as I can see in error logs.
So my question is it possible to hide all (system wide with root access) or just wait until there are any toasts on the screen or maybe if it is possible to set focus to the activity view hierarchy manually.
I would be grateful for any help with this problem.
Thank you.

P.S. Disabling toast directly somewhere in my app is not an option because it brings some extra logic into the app which is only required while testing.

Seagoing answered 13/8, 2015 at 20:52 Comment(4)
Your P.S made me happy, keep it up. As for the question, does Espresso offer any type of waitForCondition clauses, so you can have an easy timeout for toasts to disappear?Khabarovsk
Thread.sleep works fine only for one toast, AFAIK LONG time is 3.5seconds , but what to do if there several toast sequentially are being showed and it takes much more time, it would be grate if there was any way to set focus back to the activitySeagoing
If you do something like this https://mcmap.net/q/173663/-thread-sleep-with-espresso you can have a longer timeout that won't necessarily take all the time.Khabarovsk
I would have a look at this answer: https://mcmap.net/q/212055/-checking-toast-message-in-android-espressoDaegal
T
9

You can let Espresso wait until all toasts are disappeared with a custom idling resource.

Here I use CountingIdlingResource which is a idling resource managing a counter: when the counter changes from non-zero to zero it notifies the transition callback.

Here is a complete example; the key points follow:

public final class ToastManager {
    private static final CountingIdlingResource idlingResource = new CountingIdlingResource("toast");
    private static final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() {
        @Override
        public void onViewAttachedToWindow(final View v) {
            idlingResource.increment();
        }

        @Override
        public void onViewDetachedFromWindow(final View v) {
            idlingResource.decrement();
        }
    };

    private ToastManager() { }

    public static Toast makeText(final Context context, final CharSequence text, final int duration) {
        Toast t = Toast.makeText(context, text, duration);
        t.getView().addOnAttachStateChangeListener(listener);
        return t;
    }

    // For testing
    public static IdlingResource getIdlingResource() {
        return idlingResource;
    }
}

How to show the toast:

ToastManager.makeText(this, "Third", Toast.LENGTH_SHORT).show();

How to set-up/tear-down a test:

@Before
public void setUp() throws Exception {
    super.setUp();
    injectInstrumentation(InstrumentationRegistry.getInstrumentation());
    Espresso.registerIdlingResources(ToastManager.getIdlingResource());
    getActivity();
}

@After
public void tearDown() throws Exception {
    super.tearDown();
    Espresso.unregisterIdlingResources(ToastManager.getIdlingResource());
}
Tippler answered 15/8, 2015 at 9:53 Comment(2)
This waits for toasts to end instead of cancelling, beside not working if toasts are made by a third-party library.Stomodaeum
But the root problem is Android's poor-design (where "poor" means not having required API). Toasts have not any global-manager and/or service yet, else there would be something like ActivityLifecycleCallbacks API (but for toasts instead of activities).Stomodaeum
P
3

I have not found any perfect solution to this, but the best is to make a mToast member variable visible for testing, and use that to cancel any active toast in @After, like this:

When showing toast (the production code for the Activity under test):

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
Toast mToast;

private void showToast(final String text) {
    mToast = Toast.makeText(this, text, Toast.LENGTH_LONG);
    mToast.show();
}

The test code (in the same package as the code under test):

    @After
    public void tearDown() {
        // Remove any toast message that is still shown:
        Toast toast = mActivityRule.getActivity().mToast;
        if (toast != null) {
            toast.cancel();
        }
    }

This will require you to change the production code a tiny bit, but using @VisibleForTesting in the latest version of Android Studio will give error if you use the member variable elsewhere.

Pyrogallol answered 6/6, 2017 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.