android android-espresso toast

Comprobación del mensaje de tostadas en Android espresso



android-espresso toast (10)

¿Alguien sabría cómo probar la aparición de un mensaje Toast en Android espresso? En robotium es fácil y lo usé, pero comencé a trabajar en espresso pero no recibí el comando exacto.


Aunque la pregunta tiene una respuesta aceptada, que por cierto no funciona para mí, me gustaría agregar mi solución en Kotlin, que obtuve de la respuesta de Thomas R.

package somepkg import android.support.test.espresso.Espresso.onView import android.support.test.espresso.Root import android.support.test.espresso.matcher.ViewMatchers.withText import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY import android.view.WindowManager.LayoutParams.TYPE_TOAST import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.TypeSafeMatcher /** * This class allows to match Toast messages in tests with Espresso. * * Idea taken from: https://.com/a/33387980 * * Usage in test class: * * import somepkg.ToastMatcher.Companion.onToast * * // To assert a toast does *not* pop up: * onToast("text").check(doesNotExist()) * onToast(textId).check(doesNotExist()) * * // To assert a toast does pop up: * onToast("text").check(matches(isDisplayed())) * onToast(textId).check(matches(isDisplayed())) */ class ToastMatcher(private val maxFailures: Int = DEFAULT_MAX_FAILURES) : TypeSafeMatcher<Root>() { /** Restrict number of false results from matchesSafely to avoid endless loop */ private var failures = 0 override fun describeTo(description: Description) { description.appendText("is toast") } public override fun matchesSafely(root: Root): Boolean { val type = root.windowLayoutParams.get().type @Suppress("DEPRECATION") // TYPE_TOAST is deprecated in favor of TYPE_APPLICATION_OVERLAY if (type == TYPE_TOAST || type == TYPE_APPLICATION_OVERLAY) { val windowToken = root.decorView.windowToken val appToken = root.decorView.applicationWindowToken if (windowToken === appToken) { // windowToken == appToken means this window isn''t contained by any other windows. // if it was a window for an activity, it would have TYPE_BASE_APPLICATION. return true } } // Method is called again if false is returned which is useful because a toast may take some time to pop up. But for // obvious reasons an infinite wait isn''t of help. So false is only returned as often as maxFailures specifies. return (++failures >= maxFailures) } companion object { /** Default for maximum number of retries to wait for the toast to pop up */ private const val DEFAULT_MAX_FAILURES = 5 fun onToast(text: String, maxRetries: Int = DEFAULT_MAX_FAILURES) = onView(withText(text)).inRoot(isToast(maxRetries))!! fun onToast(textId: Int, maxRetries: Int = DEFAULT_MAX_FAILURES) = onView(withText(textId)).inRoot(isToast(maxRetries))!! fun isToast(maxRetries: Int = DEFAULT_MAX_FAILURES): Matcher<Root> { return ToastMatcher(maxRetries) } } }

Espero que esto sea de ayuda para los lectores posteriores: el uso se describe en el comentario.


Escribo mi tostador personalizado:

import android.view.WindowManager import androidx.test.espresso.Root import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; class ToastMatcher : TypeSafeMatcher<Root>() { override fun describeTo(description: Description) { description.appendText("is toast") } override fun matchesSafely(root: Root): Boolean { val type = root.getWindowLayoutParams().get().type if (type == WindowManager.LayoutParams.TYPE_TOAST) { val windowToken = root.getDecorView().getWindowToken() val appToken = root.getDecorView().getApplicationWindowToken() if (windowToken === appToken) { return true } } return false } }

Y usa así:

onView(withText(R.string.please_input_all_fields)).inRoot(ToastMatcher()).check(matches(isDisplayed()))


Esta declaración un poco larga funciona para mí:

import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; .... onView(withText(R.string.TOAST_STRING)).inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView())))).check(matches(isDisplayed()));


La forma en que se implementan las tostadas permite detectar que se ha mostrado una tostada. Sin embargo, no hay forma de ver si se ha solicitado un Toast, a través de una llamada para mostrar ()) o para bloquear entre el período de tiempo entre show () y cuando el brindis se ha hecho visible. Esto abre problemas de tiempo irresolubles (que solo puede abordar a través del sueño y la esperanza).

Si realmente quieres verificar esto, aquí hay una alternativa no tan bonita usando Mockito y un espía de prueba:

public interface Toaster { public void showToast(Toast t); private static class RealToaster { @Override public void showToast(Toast t) { t.show(); } public static Toaster makeToaster() { return new RealToaster(); } } Then in your test public void testMyThing() { Toaster spyToaster = Mockito.spy(Toaster.makeToaster()); getActivity().setToaster(spyToaster); onView(withId(R.button)).perform(click()); getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { // must do this on the main thread because the matcher will be interrogating a view... Mockito.verify(spyToaster).showToast(allOf(withDuration(Toast.LENGTH_SHORT), withView(withText("hello world")); }); } // create a matcher that calls getDuration() on the toast object Matcher<Toast> withDuration(int) // create a matcher that calls getView() and applies the given view matcher Matcher<Toast> withView(Matcher<View> viewMatcher) another answer regarding this if(someToast == null) someToast = Toast.makeText(this, "sdfdsf", Toast.LENGTH_LONG); boolean isShown = someToast.getView().isShown();


La respuesta aceptada es buena pero no funcionó para mí. Así que busqué un poco y encontré este artículo de blog . Esto me dio una idea de cómo hacerlo y actualicé la solución anterior.

Primero implementé el ToastMatcher:

import android.os.IBinder; import android.support.test.espresso.Root; import android.view.WindowManager; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; public class ToastMatcher extends TypeSafeMatcher<Root> { @Override public void describeTo(Description description) { description.appendText("is toast"); } @Override public boolean matchesSafely(Root root) { int type = root.getWindowLayoutParams().get().type; if (type == WindowManager.LayoutParams.TYPE_TOAST) { IBinder windowToken = root.getDecorView().getWindowToken(); IBinder appToken = root.getDecorView().getApplicationWindowToken(); if (windowToken == appToken) { // windowToken == appToken means this window isn''t contained by any other windows. // if it was a window for an activity, it would have TYPE_BASE_APPLICATION. return true; } } return false; } }

Luego implementé mis métodos de verificación como este:

public void isToastMessageDisplayed(int textId) { onView(withText(textId)).inRoot(MobileViewMatchers.isToast()).check(matches(isDisplayed())); }

MobileViewMatchers es un contenedor para acceder a los comparadores. Allí isToast() método estático isToast() .

public static Matcher<Root> isToast() { return new ToastMatcher(); }

Esto funciona como un encanto para mí.


Primero asegúrese de importar:

import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.support.test.espresso.matcher.RootMatchers.withDecorView; import static android.support.test.espresso.assertion.ViewAssertions.matches;

Dentro de tu clase probablemente tengas una regla como esta:

@Rule public ActivityTestRule<MyNameActivity> activityTestRule = new ActivityTestRule<>(MyNameActivity.class);

Dentro de tu prueba:

MyNameActivity activity = activityTestRule.getActivity(); onView(withText(R.string.toast_text)). inRoot(withDecorView(not(is(activity.getWindow().getDecorView())))). check(matches(isDisplayed()));

Esto funcionó para mí, y fue bastante fácil de usar.


Primero cree un Cutom Toast Matcher que podamos usar en nuestros casos de prueba:

public class ToastMatcher extends TypeSafeMatcher<Root> { @Override public void describeTo(Description description) { description.appendText("is toast"); } @Override public boolean matchesSafely(Root root) { int type = root.getWindowLayoutParams().get().type; if ((type == WindowManager.LayoutParams.TYPE_TOAST)) { IBinder windowToken = root.getDecorView().getWindowToken(); IBinder appToken = root.getDecorView().getApplicationWindowToken(); if (windowToken == appToken) { //means this window isn''t contained by any other windows. } } return false; } }

1. Pruebe si se muestra el mensaje Toast

onView(withText(R.string.mssage)).inRoot(new ToastMatcher()) .check(matches(isDisplayed()));

2. Pruebe si el mensaje Toast no se muestra

onView(withText(R.string.mssage)).inRoot(new ToastMatcher()) .check(matches(not(isDisplayed())));

3. ID de prueba de que Toast contiene un mensaje de texto específico

onView(withText(R.string.mssage)).inRoot(new ToastMatcher()) .check(matches(withText("Invalid Name"));

Gracias Anuja

Nota: esta respuesta es de esta POST.


Si está utilizando las herramientas de prueba de Android más nuevas de Jetpack , sabe que ActivityTestRule está en desuso y debe usar ActivityScenario o ActivityScenarioRule (que contiene el primero).

Prerrequisitos. Cree la variable decorView y asígnela antes de las pruebas;

@Rule public ActivityScenarioRule<FeedActivity> activityScenarioRule = new ActivityScenarioRule<>(FeedActivity.class); private View decorView; @Before public void setUp() { activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction<FeedActivity>() { @Override public void perform(FeedActivityactivity) { decorView = activity.getWindow().getDecorView(); } }); }

Probarse

@Test public void given_when_thenShouldShowToast() { String expectedWarning = getApplicationContext().getString(R.string.error_empty_list); onView(withId(R.id.button)) .perform(click()); onView(withText(expectedWarning)) .inRoot(withDecorView(not(decorView)))// Here we use decorView .check(matches(isDisplayed())); }

getApplicationContext () se puede tomar de androidx.test.core.app.ApplicationProvider.getApplicationContext;


Soy bastante nuevo en esto, pero hice una clase base ''BaseTest'' que tiene todas mis acciones (deslizar, hacer clic, etc.) y verificaciones (verificar el contenido de las vistas de texto, etc.).

protected fun verifyToastMessageWithText(text: String, activityTestRule: ActivityTestRule<*>) { onView(withText(text)).inRoot(withDecorView(not(activityTestRule.activity.window.decorView))).check(matches(isDisplayed())) } protected fun verifyToastMessageWithStringResource(id: Int, activityTestRule: ActivityTestRule<*>) { onView(withText(id)).inRoot(withDecorView(not(activityTestRule.activity.window.decorView))).check(matches(isDisplayed())) }


Yo diría que los mensajes de brindis primero definen tu regla

@Rule public ActivityTestRule<AuthActivity> activityTestRule = new ActivityTestRule<>(AuthActivity.class);

entonces, sea cual sea el mensaje de mensaje que busca, escríbalo entre comillas, por ejemplo, usé "Dirección de correo electrónico no válida"

onView(withText("Invalid email address")) .inRoot(withDecorView(not(activityTestRule.getActivity().getWindow().getDecorView()))) .check(matches(isDisplayed()));