Recently I had a problem with JUnit Testing of a Toast.
In my LoginActivity class I have a Button with that calls this:
Toast.makeText(LoginActivity.this, "toast text", Toast.LENGTH_SHORT).show();
And this is a LoginActivityTest class
package com.android_pokladna.LoginActivity;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.rule.ActivityTestRule;
import com.android_pokladna.Model.Login.LoginRequest;
import com.android_pokladna.Model.Login.LoginResponse;
import com.android_pokladna.Other.TokenSaver;
import com.android_pokladna.R;
import com.android_pokladna.ShopActivity.ShopActivity;
import com.android_pokladna.api.ApiClientandService.ApiCallback;
import com.android_pokladna.api.ApiClientandService.ApiClient;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.RootMatchers.isDialog;
import static androidx.test.espresso.matcher.RootMatchers.isFocusable;
import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.*;
public class LoginActivityTest {
@Rule
public ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule(LoginActivity.class);
private LoginActivity loginActivity = null;
public static ViewAction waitFor(final long millis) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "Wait for " + millis + " milliseconds.";
}
@Override
public void perform(UiController uiController, final View view) {
uiController.loopMainThreadForAtLeast(millis);
}
};
}
@Before
public void setUp() throws Exception {
loginActivity = activityTestRule.getActivity();
//clears all system dialogs
loginActivity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
@Test
public void testDialogButtonClick() throws Throwable {
onView(withId(R.id.activityLoginButtonEnter)).perform(click());
onView(withId(R.id.activityLoginDialogButton)).perform(click());
onView(withText("TEXT")).inRoot(new ToastMatcher())
.check(matches(withText("TEXT")));
}
@After
public void tearDown() throws Exception {
loginActivity = null;
}
}
onView(withText("toast text")).inRoot(new ToastMatcher())
.check(matches(withText("toast text")));
throws an error:
androidx.test.espresso.NoMatchingRootException: Matcher 'is toast' did not match any of the following roots: [Root{application-window-token=android.view.ViewRootImpl$W@a78c87f, window-token=android.view.ViewRootImpl$W@a78c87f, has-window-focus=true, layout-params-type=2, layout-params-string={(0,0)(wrapxwrap) gr=CENTER sim={adjust=pan} ty=APPLICATION fmt=TRANSPARENT wanim=0x10302ff
fl=DIM_BEHIND SPLIT_TOUCH HARDWARE_ACCELERATED
fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR}, decor-view-string=DecorView{id=-1, visibility=VISIBLE, width=1144, height=1300, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params={(0,0)(wrapxwrap) gr=CENTER sim={adjust=pan} ty=APPLICATION fmt=TRANSPARENT wanim=0x10302ff
fl=DIM_BEHIND SPLIT_TOUCH HARDWARE_ACCELERATED
fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR}, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}, Root{application-window-token=android.view.ViewRootImpl$W@3be404c, window-token=android.view.ViewRootImpl$W@3be404c, has-window-focus=false, layout-params-type=1, layout-params-string={(0,0)(fillxfill) sim={adjust=pan} ty=BASE_APPLICATION wanim=0x10302fe
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
pfl=FORCE_DRAW_STATUS_BAR_BACKGROUND FIT_INSETS_CONTROLLED
fitSides=}, decor-view-string=DecorView{id=-1, visibility=VISIBLE, width=2560, height=1600, has-focus=false, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params={(0,0)(fillxfill) sim={adjust=pan} ty=BASE_APPLICATION wanim=0x10302fe
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
pfl=FORCE_DRAW_STATUS_BAR_BACKGROUND FIT_INSETS_CONTROLLED
fitSides=}, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=3}}]
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:1736)
at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:12)
at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:7)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:8)
at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:12)
at com.android_pokladna.LoginActivity.LoginActivityTest.testDialogButtonClick(LoginActivityTest.java:116)
Tests ran to completion.
In the test class I use ToastMatcher class which looks like this:
package com.android_pokladna.LoginActivity;
import android.os.IBinder;
import android.view.WindowManager;
import androidx.test.espresso.Root;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class ToastMatcher extends TypeSafeMatcher<Root> {
/*
Author: http://www.qaautomated.com/2016/01/how-to-test-toast-message-using-espresso.html
*/
@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) {
return true;
}
}
return false;
}
}
I already saw that many people have similar problem. I did this according to one video where test is successfully done: https://www.youtube.com/watch?v=L037q8MGkGA&ab_channel=CodingWithMitch
His videos are really helpful but I don't understand the error I am getting.
The test takes a while to complete and when toast is getting tested. It is already gone and maybe that is why it is throwing an error.
If anyone could help me, I would really appreciate that. Thank you for your help!