Würde jemand wissen, wie man das Aussehen einer Toast-Nachricht in Android-Espresso testet? In Robotium ist es einfach und ich habe es benutzt, aber angefangen, mit Espresso zu arbeiten, aber nicht den genauen Befehl bekommen.
android
android-espresso
toast
Humayun Rana
quelle
quelle
Antworten:
Diese etwas lange Aussage funktioniert bei mir:
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()));
quelle
is()
Methode ist redundantActivityTestRule
. Sie können die Aktivität aus dieser Regel mit abrufenActivityTestRule#getActivity()
.onView(withText(R.string.toast_text)).inRoot(withDecorView(not(mActivityRule.getActivity().getWindow().getDecorView()))).check(matches(isDisplayed()));
Die akzeptierte Antwort ist gut, hat aber bei mir nicht funktioniert. Also habe ich ein bisschen gesucht und diesen Blog-Artikel gefunden . Dies gab mir eine Idee, wie es geht, und ich aktualisierte die obige Lösung.
Zuerst habe ich den ToastMatcher implementiert:
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; } }
Dann habe ich meine Prüfmethoden folgendermaßen implementiert:
public void isToastMessageDisplayed(int textId) { onView(withText(textId)).inRoot(MobileViewMatchers.isToast()).check(matches(isDisplayed())); }
MobileViewMatchers ist ein Container für den Zugriff auf die Matcher. Dort habe ich die statische Methode definiert
isToast()
.public static Matcher<Root> isToast() { return new ToastMatcher(); }
Das funktioniert wie ein Zauber für mich.
quelle
Stellen Sie zunächst sicher, dass Sie Folgendes importieren:
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;
In Ihrer Klasse haben Sie wahrscheinlich eine Regel wie die folgende:
@Rule public ActivityTestRule<MyNameActivity> activityTestRule = new ActivityTestRule<>(MyNameActivity.class);
In Ihrem Test:
MyNameActivity activity = activityTestRule.getActivity(); onView(withText(R.string.toast_text)). inRoot(withDecorView(not(is(activity.getWindow().getDecorView())))). check(matches(isDisplayed()));
Das hat bei mir funktioniert und war ziemlich einfach zu bedienen.
quelle
Wenn Sie die neuesten Android-Testtools von Jetpack verwenden , wissen Sie, dass ActivityTestRule veraltet ist und Sie ActivityScenario oder ActivityScenarioRule (das erste enthält) verwenden sollten.
Voraussetzungen. Erstellen Sie eine decorView-Variable und weisen Sie sie vor den Tests zu.
@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(); } }); }
Testen Sie sich
@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 () kann entnommen werden
androidx.test.core.app.ApplicationProvider.getApplicationContext;
quelle
withText()
.inRoot(withDecorView(not(activityRule.activity.window.decorView)))
ActivityScenarioRule
. In diesem Beispiel funktioniert Ihr Code nicht.Obwohl die Frage eine akzeptierte Antwort hat - was übrigens für mich nicht funktioniert - möchte ich meine Lösung in Kotlin hinzufügen, die ich aus der Antwort von Thomas R. abgeleitet habe:
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://stackoverflow.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) } } }
Ich hoffe, dass dies für spätere Leser hilfreich sein wird - die Verwendung wird im Kommentar beschrieben.
quelle
Erstellen Sie zuerst einen Cutom Toast Matcher, den wir in unseren Testfällen verwenden können -
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 true; } } return false; } }
1. Testen Sie, ob die Toastnachricht angezeigt wird
onView(withText(R.string.mssage)).inRoot(new ToastMatcher()) .check(matches(isDisplayed()));
2. Testen Sie, ob die Toastnachricht nicht angezeigt wird
onView(withText(R.string.mssage)).inRoot(new ToastMatcher()) .check(matches(not(isDisplayed())));
3. Test-ID Der Toast enthält eine bestimmte Textnachricht
onView(withText(R.string.mssage)).inRoot(new ToastMatcher()) .check(matches(withText("Invalid Name"));
Danke, Anuja
Hinweis - Diese Antwort stammt aus diesem POST.
quelle
Ich schreibe meinen benutzerdefinierten Toast Matcher:
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 } }
Und verwenden Sie wie folgt:
onView(withText(R.string.please_input_all_fields)).inRoot(ToastMatcher()).check(matches(isDisplayed()))
quelle
Ich würde sagen, für Toastnachrichten definieren Sie zuerst Ihre Regel
@Rule public ActivityTestRule<AuthActivity> activityTestRule = new ActivityTestRule<>(AuthActivity.class);
Was auch immer Sie für einen Toastnachrichtentext suchen, geben Sie ihn zwischen den Zitaten ein. Ich habe beispielsweise "Ungültige E-Mail-Adresse" verwendet.
onView(withText("Invalid email address")) .inRoot(withDecorView(not(activityTestRule.getActivity().getWindow().getDecorView()))) .check(matches(isDisplayed()));
quelle
Ich bin ziemlich neu in diesem Bereich, aber ich habe eine Basisklasse 'BaseTest' erstellt, die alle meine Aktionen (Wischen, Klicken usw.) und Überprüfungen (Überprüfen der Textansichten auf Inhalt usw.) enthält.
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())) }
quelle
onView (withId (R.id.inputField)). check (Übereinstimmungen (withText ("Lalala")));
quelle
Ich möchte eine alternative Methode vorschlagen, insbesondere wenn Sie überprüfen müssen, ob ein bestimmter Toast NICHT angezeigt wird
Das Problem hier ist das
onView(viewMatcher) .inRoot(RootMatchers.isPlatformPopup()) .check(matches(not(isDisplayed())))
oder
onView(viewMatcher) .inRoot(RootMatchers.isPlatformPopup()) .check(doesNotExist())
oder andere benutzerdefinierte
inRoot
Überprüfungen werden ausgelöst,NoMatchingRootException
noch bevor der Code an diecheck
Methode übergeben wirdSie können nur die Ausnahme abfangen und den Test abschließen, aber das ist keine gute Option, da das Werfen und Fangen
NoMatchingRootException
im Vergleich zum Standardtestfall viel Zeit in Anspruch nimmt. Espresso scheint eine Weile auf die Wurzel zu wartenFür diesen Fall wird empfohlen, hier nur auf Espresso zu verzichten und
UiAutomator
für diese Behauptung zu verwenden. DieEspresso
undUiAutomator
Frameworks können problemlos in einer Umgebung zusammenarbeiten.val device: UiDevice get() = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) fun assertPopupIsNotDisplayed() { device.waitForIdle() assertFalse(device.hasObject(By.text(yourText)))) } fun assertPopupIsDisplayed() { device.waitForIdle() assertTrue(device.hasObject(By.text(yourText)))) }
quelle
Für Kotlin musste ich die Apply-Erweiterungsfunktion verwenden, und das funktionierte für mich.
1- Deklarieren Sie Ihre ToastMatcher-Klasse im androidTest-Ordner:
class ToastMatcher : TypeSafeMatcher<Root?>() { override fun matchesSafely(item: Root?): Boolean { val type: Int? = item?.windowLayoutParams?.get()?.type if (type == WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW) { val windowToken: IBinder = item.decorView.windowToken val appToken: IBinder = item.decorView.applicationWindowToken if (windowToken === appToken) { // means this window isn't contained by any other windows. return true } } return false } override fun describeTo(description: Description?) { description?.appendText("is toast") } }
2- Dann testen Sie auf diese Weise, ob die Toastnachricht tatsächlich angezeigt wird
onView(withText(R.string.invalid_phone_number)) .inRoot(ToastMatcher().apply { matches(isDisplayed()) });
Zuordnung zur ToastMatcher-Klasse:
/** * Author: http://www.qaautomated.com/2016/01/how-to-test-toast-message-using-espresso.html */
quelle
Verwenden von ActivityScenarioRule und Java
Einige Importe für den Code
import android.view.View; import androidx.test.ext.junit.rules.ActivityScenarioRule; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.matcher.RootMatchers.withDecorView; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.not;
1. Deklarieren Sie die Regel
//Change YourActivity by the activity you are testing @Rule public ActivityScenarioRule<YourActivity> activityRule = new ActivityScenarioRule<>(YourActivity.class);
2. Initialisieren Sie die Dekoransicht
private View decorView; @Before public void loadDecorView() { activityRule.getScenario().onActivity( activity -> decorView = activity.getWindow().getDecorView() ); }
3. Testen Sie es schließlich
@Test public void testWithToasts() { //Arrange and act code //Modify toast_msg to your own string resource onView(withText(R.string.toast_msg)). inRoot(RootMatchers.withDecorView(not(decorView))) .check(matches(isDisplayed())); }
quelle
Die Art und Weise, wie Toasts implementiert werden, ermöglicht es, zu erkennen, dass ein Toast angezeigt wurde. Es gibt jedoch keine Möglichkeit zu sehen, ob ein Toast angefordert wurde, durch einen Aufruf von show ()) oder zu blockieren zwischen dem Zeitraum zwischen show () und dem Zeitpunkt, zu dem der Toast sichtbar geworden ist. Dies führt zu unlösbaren Timing-Problemen (die Sie nur durch Schlaf und Hoffnung angehen können).
Wenn Sie dies wirklich wirklich überprüfen möchten, finden Sie hier eine nicht ganz so schöne Alternative mit Mockito und einem Testspion:
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();
quelle