Android Espresso测试Toast报NoMatchingRootException异常求解
问题根因
- 你使用的旧版
ToastMatcher仅适配API 30以下系统,Android 11(API 30)后系统Toast的窗口类型不再是TYPE_TOAST,导致匹配逻辑完全失效 - 测试执行速度快于Toast弹出速度,校验逻辑执行时Toast还未完成渲染,或已经消失
可行解决方案
方案1:使用兼容高版本系统的ToastMatcher+加等待逻辑
- 替换原有
ToastMatcher为兼容版:
import android.os.Build; 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> { @Override public void describeTo(Description description) { description.appendText("is toast"); } @Override public boolean matchesSafely(Root root) { int type = root.getWindowLayoutParams().get().type; boolean isToastWindowType = false; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { isToastWindowType = type == WindowManager.LayoutParams.TYPE_TOAST; } else { isToastWindowType = type == WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } if (isToastWindowType) { IBinder windowToken = root.getDecorView().getWindowToken(); IBinder appToken = root.getDecorView().getApplicationWindowToken(); return windowToken == appToken; } return false; } }
- 修改测试用例,点击触发Toast的按钮后加等待逻辑,你已实现的
waitFor方法可以直接用:
@Test public void testDialogButtonClick() throws Throwable { onView(withId(R.id.activityLoginButtonEnter)).perform(click()); onView(withId(R.id.activityLoginDialogButton)).perform(click()); // 等待1秒,保证Toast已弹出且未消失 onView(isRoot()).perform(waitFor(1000)); onView(withText("toast text")).inRoot(new ToastMatcher()) .check(matches(isDisplayed())); }
方案2:使用Espresso自带匹配器(无需自定义ToastMatcher,更省心)
不用自己维护ToastMatcher的版本兼容逻辑,直接通过匹配非Activity窗口的方式识别Toast:
@Test public void testDialogButtonClick() throws Throwable { onView(withId(R.id.activityLoginButtonEnter)).perform(click()); onView(withId(R.id.activityLoginDialogButton)).perform(click()); onView(isRoot()).perform(waitFor(1000)); // 直接匹配不属于当前Activity的窗口 onView(withText("toast text")) .inRoot(withDecorView(not(is(activityTestRule.getActivity().getWindow().getDecorView())))) .check(matches(isDisplayed())); }
注意事项
- 测试过程中保持应用在前台、屏幕亮屏,后台状态下系统不会弹出Toast
- 模拟器/测试机关闭应用的通知拦截权限,部分定制ROM会默认禁止第三方应用弹出Toast
内容的提问来源于stack exchange,提问作者Forged
相关产品推荐
相关产品推荐

