我尝试使用Espresso IdlingResource
等待Activity.onCreate
中启动的后台任务。
我看到了类似问题here和there,但建议的解决方案对我来说并不适用。
我在waitForIdle(long timeout)
中添加了一个Activity
方法,我从我的@Before
方法中调用了这种方法,这种方法有效,但在应用程序代码中有点侵入性,IdlingResource
是就是为了这个目的。
根据给定的链接,我了解@Before
在Activity.onCreate
后调用,然后我们注册IdlingResource
太晚了。
我的IdlingResource
需要Activity
个实例来确定它是否空闲。使用ActivityTestRule.beforeActivityLaunched
只允许我创建一个"空" IdlingResource
(此时getActivity()
为null
)并注册。然后在@Before
我将Activity
设置为我的IdlingResource
,但Espresso仍然没有等待它。 isIdleNow
甚至没有被调用。
(我并不热衷于为此目的覆盖测试运行器的想法)
以下是用于测试的片段:
MainActivity
:
public class MainActivity extends AppCompatActivity
{
private Object mIdleMonitor = new Object();
private boolean mIdle;
private TextView mHello;
private View mProgress;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHello = findViewById(R.id.main_hello);
mProgress = findViewById(R.id.main_progress);
toggleWorkingState(true);
new Thread(new Runnable()
{
@Override
public void run()
{
SystemClock.sleep(5000);
runOnUiThread(new Runnable()
{
@Override
public void run()
{
mHello.setText(R.string.main_hello_label);
toggleWorkingState(false);
}
});
}
}).start();
}
@UiThread
private void toggleWorkingState(boolean working)
{
mProgress.setVisibility(working ? View.VISIBLE : View.GONE);
synchronized(mIdleMonitor)
{
mIdle = !working;
mIdleMonitor.notify();
}
}
public boolean isIdle()
{
synchronized(mIdleMonitor)
{
return mIdle;
}
}
public void waitForIdle(long timeout)
{
long end = System.currentTimeMillis() + timeout;
synchronized(mIdleMonitor)
{
while(!mIdle && System.currentTimeMillis() < end)
{
try
{
mIdleMonitor.wait(timeout);
}
catch(InterruptedException e)
{
break;
}
}
}
}
}
MainActivityIdlingResource
:
public class MainActivityIdlingResource implements IdlingResource
{
private static int ID;
private final int mId;
private WeakReference<MainActivity> mObservable;
@Nullable
private ResourceCallback mCallback;
private boolean mWasIdle = false;
public MainActivityIdlingResource()
{
mId = ++ID;
}
public void setActivity(MainActivity observable)
{
mObservable = new WeakReference<>(observable);
}
@Override
public String getName()
{
return MainActivityIdlingResource.class.getSimpleName() + "#" + mId;
}
@Override
public boolean isIdleNow()
{
MainActivity observable = mObservable == null ? null : mObservable.get();
boolean isIdle = observable != null && observable.isIdle();
if (!mWasIdle && isIdle && mCallback != null)
{
mCallback.onTransitionToIdle();
}
mWasIdle = isIdle;
return isIdle;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback callback)
{
mCallback = callback;
}
}
MainActivityTest
:
@RunWith(AndroidJUnit4.class)
public class MainActivityTest
{
private IdlingRegistry mIdlingRegistry = IdlingRegistry.getInstance();
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<MainActivity>(MainActivity.class)
{
@Override
protected void beforeActivityLaunched()
{
super.beforeActivityLaunched();
mIdlingResource = new MainActivityIdlingResource();
mIdlingRegistry.register(mIdlingResource);
}
@Override
protected void afterActivityFinished()
{
super.afterActivityFinished();
if (mIdlingResource != null)
{
mIdlingRegistry.unregister(mIdlingResource);
}
}
};
private MainActivity mActivity;
private MainActivityIdlingResource mIdlingResource;
/**
* Source: https://stackoverflow.com/a/37864603/2551689
*/
public static ViewAction replaceProgressBarDrawable()
{
return actionWithAssertions(new ViewAction()
{
@Override
public Matcher<View> getConstraints()
{
return isAssignableFrom(ProgressBar.class);
}
@Override
public String getDescription()
{
return "replace the ProgressBar drawable";
}
@Override
public void perform(final UiController uiController, final View view)
{
// Replace the indeterminate drawable with a static red ColorDrawable
ProgressBar progressBar = (ProgressBar) view;
progressBar.setIndeterminateDrawable(new ColorDrawable(0xffff0000));
uiController.loopMainThreadUntilIdle();
}
});
}
@Before
public void setUp()
{
mActivity = mActivityRule.getActivity();
mIdlingResource.setActivity(mActivity);
// using this watcher, UiController.loopMainThreadUntilIdle() fixes the idle issue, don't understand why nor if it's mandatory
// onView(withId(R.id.main_progress)).perform(replaceProgressBarDrawable());
// using mActivity.waitForIdle(6000); do the job but is not the expected solution
}
@Test
public void test_idle_state()
{
assertTrue(mActivity.isIdle());
onView(withId(R.id.main_progress)).check(matches(not(isDisplayed())));
onView(withText(R.string.main_hello_label)).check(matches(isDisplayed()));
}
}