我刚开始使用Espresso recorder
。我做了我的第一个测试,从我可以看到的功能onView
,那就是等待对象继续不会完成工作。它总是返回:
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching
.
是否有任何功能可以作为我可以使用的wait for
package com.mytest;
import android.support.test.espresso.ViewInteraction;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.IdlingResource;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Before;
import org.junit.runner.RunWith;
import com.mytest.R;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static org.hamcrest.Matchers.allOf;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class Test1 {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
private IdlingResource mIdlingResource;
@Before
public void registerIdlingResource() {
mIdlingResource = mActivityRule.getActivity().getIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
@Test
public void test1() {
ViewInteraction recyclerView = onView(
allOf(withId(R.id.recycler_view), isDisplayed()));
recyclerView.perform(actionOnItemAtPosition(0, click()));
ViewInteraction relativeLayout = onView(
allOf(withId(R.id.capture_layout), isDisplayed()));
relativeLayout.perform(click());
ViewInteraction relativeLayout2 = onView(
allOf(withId(R.id.like_layout),
withParent(allOf(withId(R.id.cameraLayout),
withParent(withId(android.R.id.content)))),
isDisplayed()));
relativeLayout2.perform(click());
ViewInteraction relativeLayout3 = onView(
allOf(withId(R.id.exit_layout), isDisplayed()));
relativeLayout3.perform(click());
}
}
一种方法是使用匹配器循环,直到它发生。当我需要等待异步任务完成时,我使用以下帮助程序。
public class ViewSynchronizer {
private static final String TAG = "MC_Synchronizer";
public static boolean viewExists(final Matcher<View> viewMatcher, final long millis) throws InterruptedException {
final Boolean[] found = new Boolean[1];
final CountDownLatch latch = new CountDownLatch(1);
ViewAction action = new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "wait for a specific view with id <" + viewMatcher.toString() + "> during " + millis + " millis.";
}
@Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
final long startTime = System.currentTimeMillis();
final long endTime = startTime + millis;
do {
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
if (viewMatcher.matches(child)) {
Log.d(TAG, "perform: found match");
found[0] = true;
latch.countDown();
return;
}
}
uiController.loopMainThreadForAtLeast(50);
}
while (System.currentTimeMillis() < endTime);
found[0] = false;
latch.countDown();
}
};
onView(isRoot()).perform(action);
latch.await();
return found[0];
}
}
用法:
Assert.assertTrue(viewExists(allOf(withId(R.id.account_sign_out_btb),withEffectiveVisibility(ViewMatchers.Visibility.GONE)),2000)); //wait 2 seconds
更新:看起来您正在使用回收器视图,请尝试以下代码。
此外,如果您进行任何网络调用,则需要实现 RecyclerView/Network IdlingResource 以告诉 Espresso 在执行测试步骤之前等待数据填充。
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, true, true);
private MainActivity mMainActivity = null;
private IdlingResource mIdlingResource;
@Before
public void registerIdlingResource() {
mMainActivity = mActivityRule.getActivity();
}
@Test
public void test1() {
mIdlingResource = mActivityRule.getActivity().getIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
mActivityRule.launchActivity(MainActivity.createIntent(getTargetContext()));
ViewInteraction recyclerView = onView(
allOf(withId(R.id.recycler_view), isDisplayed()));
recyclerView.perform(actionOnItemAtPosition(0, click()));
ViewInteraction relativeLayout = onView(
allOf(withId(R.id.capture_layout), isDisplayed()));
relativeLayout.perform(click());
ViewInteraction relativeLayout2 = onView(
allOf(withId(R.id.like_layout),
withParent(allOf(withId(R.id.cameraLayout),
withParent(withId(android.R.id.content)))),
isDisplayed()));
relativeLayout2.perform(click());
ViewInteraction relativeLayout3 = onView(
allOf(withId(R.id.exit_layout), isDisplayed()));
relativeLayout3.perform(click());
Espresso.unregisterIdlingResources(mIdlingResource);
}
使用ActivityTestRule
您需要设置 initialTouchMode 和 launchActivity。
使用这个 👇🏽
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, true, true);
另外,不要忘记在测试方法中取消注册@After空闲资源。
我只是调整了 MiguelSlv 的解决方案,像onView
一样返回ViewInteraction
。
所以现在我们有一个等效于 onView
的onDelayedView
,但它确实等待视图存在于层次结构🙂中
/**
* Special version of `onView`.
*
* Sometimes onView produces an exception because the view isn't ready on the hierarchy.
* It will wait for the view to exist and then resolve using `onView`.
*/
fun onDelayedView(viewMatcher: Matcher<View?>, millis: Long = 500): ViewInteraction {
val latch = CountDownLatch(1)
val action: ViewAction = object : ViewAction {
override fun getConstraints(): Matcher<View> {
return isRoot()
}
override fun getDescription(): String {
return "Wait for a view with id <$viewMatcher> to exist, during $millis millis."
}
override fun perform(uiController: UiController, view: View) {
uiController.loopMainThreadUntilIdle()
val startTime = System.currentTimeMillis()
val endTime = startTime + millis
do {
for (child in TreeIterables.breadthFirstViewTraversal(view)) {
if (viewMatcher.matches(child)) {
// Found
latch.countDown()
return
}
}
uiController.loopMainThreadForAtLeast(100)
} while (System.currentTimeMillis() < endTime)
// Not found
throw PerformException.Builder()
.withActionDescription(description)
.withViewDescription(HumanReadables.describe(view))
.withCause(TimeoutException())
.build()
}
}
onView(isRoot()).perform(action)
latch.await()
return onView(viewMatcher)
}
它不再使用"found"标志,因为如果在等待后视图不存在(默认为 500 毫秒),它会抛出PerformException
。
用法:
onDelayedView(withId(R.id.your_view_id_here))
.perform(/* your preferred view actions */)