Could not launch intent Intent - android

Here my Espresso's tests:
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import java.util.concurrent.TimeUnit
#RunWith(AndroidJUnit4::class)
class TradersActivityTest {
private lateinit var mockServer: MockWebServer
#Rule
#JvmField
var tradersIntentTestRule = IntentsTestRule(TradersActivity::class.java, false, false)
#Before
fun setup() {
mockServer = MockWebServer()
mockServer.start(8081)
}
#Test
fun hasTraders_noTradersTextView_isNotDisplayed() {
mockServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody(FileUtil.getStringFromFile(context, ONE_TRADER_NO_WALLETS_LIST)));
tradersIntentTestRule.launchActivity(Intent())
onView(withId(R.id.noTradersTextView))
.check(matches(not(isDisplayed())))
}
#Test
fun toolBar_height() {
onView(withId(R.id.toolBar))
.check(matches(withHeightResId(R.dimen.tool_bar_height)))
}
Test hasTraders_noTradersTextView_isNotDisplayed success pass.
But test toolBar_height fail with message:
java.lang.RuntimeException: No activities found. Did you forget to launch the activity by calling getActivity() or startActivitySync or similar?
at androidx.test.espresso.base.RootViewPicker.waitForAtLeastOneActivityToBeResumed(RootViewPicker.java:169)
at androidx.test.espresso.base.RootViewPicker.get(RootViewPicker.java:83)
So I change setup method:
#Before
fun setup() {
mockServer = MockWebServer()
mockServer.start(8081)
mockServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody(FileUtil.getStringFromFile(context, ONE_TRADER_NO_WALLETS_LIST)));
tradersIntentTestRule.launchActivity(Intent())
Debug.d(TAG, "SUCCCESS_START_MOCKWEBSRVER")
}
Now test toolBar_height pass, but hasTraders_noTradersTextView_isNotDisplayed fail with message:
java.lang.RuntimeException: Could not launch intent Intent { flg=0x10000000 com.myproject.android.ui.activity.TradersActivity } within 45 seconds. Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1556373134135 and now the last time the queue went idle was: 1556373143180. If these numbers are the same your activity might be hogging the event queue.
at androidx.test.runner.MonitoringInstrumentation.startActivitySync(MonitoringInstrumentation.java:459)
P.S. I need to start activity in test hasTraders_noTradersTextView_isNotDisplayed because it's a specific test.
But I wan't to start activity in test toolBar_height

In my UI automated tests, I have this classes:
public class UiTestHelper {
public static Matcher<View> childAtPosition(
final Matcher<View> parentMatcher, final int position) {
return new TypeSafeMatcher<View>() {
#Override
public void describeTo(Description description) {
description.appendText("Child at position " + position + " in parent ");
parentMatcher.describeTo(description);
}
#Override
public boolean matchesSafely(View view) {
ViewParent parent = view.getParent();
return parent instanceof ViewGroup && parentMatcher.matches(parent)
&& view.equals(((ViewGroup) parent).getChildAt(position));
}
};
}
public static void clickOn(View view) {
waitUntilVisible(view);
onView(withId(view.getId())).perform(click());
}
public static void waitUntilVisible(View view) {
ViewVisibilityIdlingResource idlingResource = new ViewVisibilityIdlingResource(view, VISIBLE);
IdlingRegistry.getInstance().register(idlingResource);
}
public static void unregisterAllIdlingResources() {
IdlingRegistry idlingRegistry = IdlingRegistry.getInstance();
for (IdlingResource resource: idlingRegistry.getResources()) {
idlingRegistry.unregister(resource);
}
}
}
and
public class ViewVisibilityIdlingResource implements IdlingResource {
private final View view;
private final int expectedVisibility;
private boolean idle;
private ResourceCallback resourceCallback;
public ViewVisibilityIdlingResource(final View view, final int expectedVisibility) {
this.view = view;
this.expectedVisibility = expectedVisibility;
this.idle = false;
this.resourceCallback = null;
}
#Override
public final String getName() {
return ViewVisibilityIdlingResource.class.getSimpleName();
}
#Override
public final boolean isIdleNow() {
idle = idle || (view.getVisibility() == expectedVisibility && view.getWidth() > 0 && view.getHeight() > 0);
if (idle) {
if (resourceCallback != null) {
resourceCallback.onTransitionToIdle();
}
}
return idle;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}
Then when I want to check that a view is there I call
UiTestHelper.waitUntilVisible(view);

Related

Android PowerMockito of TextUtils returns wrong result

I have updated to Android 31 and Android Studio Dolphin and now my tests are failing because of TextUtils.isEmpty() returns wrong result.
I have this method to mock TextUtils.isEmpty().
protected void mockTextUtilsIsEmpty() {
PowerMockito.mockStatic(TextUtils.class);
PowerMockito.when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(invocation -> {
String val = (String) invocation.getArguments()[0];
return val == null || val.length() == 0;
});
}
This is my test class.
#RunWith(PowerMockRunner.class) #PrepareForTest(TextUtils.class)
public class CustomerDetailsPresenterTest extends BaseTest {
#Rule TrampolineSchedulerRule trampolineSchedulerRule = new TrampolineSchedulerRule();
#Mock GetCustomerUseCase getCustomerUseCase;
#Mock GetMenuItemsUseCase getMenuItemsUseCase;
#Mock RolesManager rolesManager;
#Mock CustomerDetailsPresenter.View view;
private CustomerDetailsPresenter presenter;
private final int customerId = 1;
#Before public void setUp() {
mockTextUtilsIsEmpty();
presenter = new CustomerDetailsPresenter(getCustomerUseCase, getMenuItemsUseCase, rolesManager);
presenter.setView(view);
}
#Test public void shouldDisplayCustomerWithEmptyData() {
// Given
CustomerDetails customerDetails = CustomerDetails.newBuilder()
.build();
// When
Mockito.when(getCustomerUseCase.execute(customerId)).thenReturn(Single.just(customerDetails));
presenter.getCustomerDetails(customerId);
//Then
Mockito.verify(view).showRefreshing();
Mockito.verify(view).hideRefreshing();
Mockito.verify(view).displayCustomerEmailUnknown();
Mockito.verify(view).displayCustomerNoteUnknown();
}
}
This is my actual class that I want to test.
public class CustomerDetailsPresenter implements Presenter{
private final GetCustomerUseCase getCustomerUseCase;
private final GetMenuItemsUseCase getMenuItemsUseCase;
private final RolesManager rolesManager;
private CompositeDisposable disposables;
private View view;
#Inject public CustomerDetailsPresenter(
GetCustomerUseCase getCustomerUseCase,
GetMenuItemsUseCase getMenuItemsUseCase,
RolesManager rolesManager
) {
this.getCustomerUseCase = getCustomerUseCase;
this.getMenuItemsUseCase = getMenuItemsUseCase;
this.rolesManager = rolesManager;
}
public void setView(View view) {
this.view = view;
}
public void getCustomerDetails(int id) {
disposables = RxUtil.initDisposables(disposables);
if (rolesManager.isUserReadOnly()) {
view.showScreenAsReadOnly();
}
view.showRefreshing();
Disposable disposable = getCustomerUseCase.execute(id)
.doOnSuccess(customerDetails -> view.hideRefreshing())
.subscribe(customerDetails -> {
if (customerDetails != null) {
if (TextUtils.isEmpty(customerDetails.getInvoiceEmail())) {
view.displayCustomerEmailUnknown();
} else {
view.displayCustomerEmail(customerDetails.getInvoiceEmail());
}
if (TextUtils.isEmpty(customerDetails.getBillingNote())) {
view.displayCustomerNoteUnknown();
} else {
view.displayCustomerNote(customerDetails.getBillingNote());
}
view.displayCustomerAddress(customerDetails);
view.displayLastModified(customerDetails);
} else {
view.hideCustomerSecondAndThirdPhones();
}
} else {
view.hideCustomerDetails();
}
},
view::handleError
);
disposables.add(disposable);
What could be the problem?
So, in this part, it always goes to the else part of the statement, and as you can in my unit test that should not happen since I am providing null to invoice email field.
if (TextUtils.isEmpty(customerDetails.getInvoiceEmail())) {
view.displayCustomerEmailUnknown();
} else {
view.displayCustomerEmail(customerDetails.getInvoiceEmail());
}
Any ideas?

Espresso's IdlingResource with Retrofit 2

I am new to Espresso, and trying to write a test on a fragment that makes a Retrofit call when it is instantiated. When the call is received, I want to check the fragment to see if a view exists. I'm using an IdlingResource for the fragment and setting up a listener to be called that transitions the ResourceCallback to idle when the response is received (followed some steps in the implementation here: https://stackoverflow.com/a/30820189/4102823).
My fragment is instantiated when the user logs in and starts up MainActivity. The problem is that I just don't think my IdlingResource is set up correctly, and I don't know what's wrong with it. It is not even constructed until after the fragment is initiated and the call is made, even though I'm registering the IdlingResource in the test's setUp() method before everything else. So, I think the main issue here is how I get the IdlingResource instantiated alongside the fragment when I run the test, not after it. Could the problem be in the #Rule? Does it start MainActivity (which creates a NewsfeedFragment instance) before the test can run on the fragment? If so, how would I use a rule with my fragment instead?
Here is my fragment:
public ProgressListener mProgressListener;
public boolean mIsProgressShown = true;
public interface ProgressListener {
void onProgressShown();
void onProgressDismissed();
}
public void setProgressListener(ProgressListener progressListener) {
mProgressListener = progressListener;
}
public NewsfeedFragment() {
}
public static NewsfeedFragment newInstance() {
return new NewsfeedFragment();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OSUtil.setTranslucentStatusBar(getActivity().getWindow(), this.getContext());
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
mRootView = (SwipeRefreshLayout) inflater.inflate(R.layout.fragment_newsfeed, container, false);
mNewsfeedFramelayout = (FrameLayout) mRootView.findViewById(R.id.newsfeed_framelayout);
mProgressView = (ProgressBar) mRootView.findViewById(R.id.newsfeed_progress);
mPageIndictorView = (FrameLayout) mRootView.findViewById(R.id.page_indicator);
mPageIndicator = (TextView) mRootView.findViewById(R.id.page_indicator_text);
mRootView.setOnRefreshListener(this);
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) mRootView.findViewById(R.id.newsfeed_viewpager);
mPageChangeListener = this;
if (savedInstanceState != null) {
mTabPosition = savedInstanceState.getInt(EXTRA_TAB_POSITION);
mViewPager.setCurrentItem(mTabPosition);
}
fetchNewsFeed();
return mRootView;
}
private void fetchNewsFeed() {
if (NetworkUtils.isConnectedToInternet(getActivity())) {
if (NetworkUtils.getService() != null) {
if (mRootView.isRefreshing()) {
dismissProgress();
} else {
showProgress();
}
showProgress();
Call<Newsfeed> call = NetworkUtils.getService().getNewsfeed();
call.enqueue(new Callback<Newsfeed>() {
#Override
public void onResponse(Call<Newsfeed> call, Response<Newsfeed> response) {
if (response.isSuccessful()) {
dismissProgress();
mNewsfeed = response.body();
FragmentManager fragmentManager = getChildFragmentManager();
mNewsfeedPagerAdapter = new NewsfeedPagerAdapter(fragmentManager, mNewsfeed.getNewsfeedItems());
}
...
private void showProgress() {
// show the progress and notify the listener
if (mProgressListener != null){
setProgressIndicatorVisible(true);
notifyListener(mProgressListener);
}
}
public void dismissProgress() {
// hide the progress and notify the listener
if (mProgressListener != null){
setProgressIndicatorVisible(false);
mIsProgressShown = false;
notifyListener(mProgressListener);
}
}
public boolean isInProgress() {
return mIsProgressShown;
}
private void notifyListener(ProgressListener listener) {
if (listener == null){
return;
}
if (isInProgress()){
listener.onProgressShown();
}
else {
listener.onProgressDismissed();
}
}
Here is the IdlingResource:
public class ProgressIdlingResource implements IdlingResource, NewsfeedFragment.ProgressListener {
private ResourceCallback mResourceCallback;
private NewsfeedFragment mNewsfeedFragment;
public ProgressIdlingResource(NewsfeedFragment fragment) {
mNewsfeedFragment = fragment;
mNewsfeedFragment.setProgressListener(this);
}
#Override
public String getName() {
return "ProgressIdlingResource";
}
#Override
public boolean isIdleNow() {
return !mNewsfeedFragment.mIsProgressShown;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mResourceCallback = callback;
}
#Override
public void onProgressShown() {
}
#Override
public void onProgressDismissed() {
if (mResourceCallback == null) {
return;
}
//Called when the resource goes from busy to idle.
mResourceCallback.onTransitionToIdle();
}
}
The fragment test:
public class NewsfeedFragmentTest {
#Before
public void setUp() throws Exception {
Espresso.registerIdlingResources(new ProgressIdlingResource((NewsfeedFragment) MainActivity.getCurrentFragment()));
}
#Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void getViewPager() throws Exception {
onView(allOf(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE),
withId(R.id.newsfeed_viewpager))).check(matches(isDisplayed()));
}
#Test
public void getNewsfeedItems() throws Exception {
onView(withId(R.id.page_indicator)).check(matches(isDisplayed()));
}
}
Retrofit is using OkHttp, and there is a standard way to setup IdlingResource for that matter. Refer to OkHttp IdlingResource

How to setup IdlingResource for item inside RecyclerView using Espresso?

I have an activity. It contains fragment, and this fragment has RecyclerView. Some data loading from server and showing inside first item of RecyclerView.
I need setup IdlingResource for this item. So when the data will be loaded from server and showing inside this item, tests must starting.
How to setup IdlingResource for item inside RecyclerView?
IdlingResource on RecyclerView
I'm not sure that's how we use it, but it works...
In YourFragment:
public interface RecyclerViewHaveDataListener {
void recyclerViewHaveData();
}
private RecyclerViewHaveDataListener callbackIdl;
#VisibleForTesting
public void registerOnCallBackIdl(RecyclerViewHaveDataListener callbackIdl){
this.callbackIdl = callbackIdl;
if (adapter.getItemCount() > 0){
this.callbackIdl.recyclerViewHaveData();
this.callbackIdl = null;
}
}
// Use this where you add your datas to your RecyclerView by your adapter
if (callbackIdl != null && adapter.getItemCount() > 0){
callbackIdl.recyclerViewHaveData();
callbackIdl = null;
}
In Your test package:
public class RecyclerViewIdlingRes implements IdlingResource, YourFragment.RecyclerViewHaveDataListener {
private String name;
private boolean isIdle;
private volatile ResourceCallback resourceCallback;
public RecyclerViewIdlingRes(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
#Override
public boolean isIdleNow() {
return isIdle;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.resourceCallback = callback;
}
#Override
public void recyclerViewHaveData() {
if (resourceCallback == null){
return;
}
isIdle = true;
resourceCallback.onTransitionToIdle();
}
}
in your test:
#Test
public void itemIsComming(){
RecyclerViewIdlingRes mIdl = new RecyclerViewIdlingRes("idlingRecyclerView");
IdlingRegistry.getInstance().register(mIdl);
Fragment fragment = activity.getSupportFragmentManager().findFragmentById(R.id.activity_main_frame_layout);
if (fragment instanceof YourFragment) {
((YourFragment)fragment).registerOnCallBack(mIdl);
}
onIdle();
IdlingRegistry.getInstance().unregister(mIdl);
// DO YOUR STUFF...
}
The good approach will be to implement your own IdlingResource that will notify Espresso when data loading is finished. You can find some example here - How to use Espresso Idling Resource for network calls

Espresso how to wait for some time(1 hour)?

In my test case I have to record for 1 hour, in robotium solo.sleep(600000) had done my work, but In espresso I am confused with IdlingResource concept. I have to start recording and wait for some time(depending on the type of test) 15mins, 60mins etc.
Equivalent code in robotium
solo.clickOnView(solo.getView("start_record"));
solo.sleep(duration * 60 * 1000);
solo.clickOnView(solo.getView("stop_record"));
I tried to use it like this in espresso
#RunWith(AndroidJUnit4.class)
#SmallTest
public class AaEspressoTest {
private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "com.absd.rec.RecorderActivity";
private static Class<?> launcherActivityClass;
private Solo solo;
private static CoreRecordingTest skyroTestRunner;
private static Class<? extends Activity> activityClass;
static {
try {
activityClass = (Class<? extends Activity>) Class.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
#Rule
public final ActivityTestRule<?> activityRule
= new ActivityTestRule<>(activityClass);
private IntentServiceIdlingResource idlingResource;
#Before
public void registerIntentServiceIdlingResource() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
idlingResource = new IntentServiceIdlingResource(instrumentation.getTargetContext());
Espresso.registerIdlingResources(idlingResource);
}
#After
public void unregisterIntentServiceIdlingResource() {
Espresso.unregisterIdlingResources(idlingResource);
}
#Test
public void testHello() throws Exception {
onView(withId(AaEspressoTest.getId("recorderpage_record"))).perform(click());
registerIntentServiceIdlingResource();
onView(withId(AaEspressoTest.getId("recorderpage_stop"))).perform(click());
}
}
Idling resource
public class IntentServiceIdlingResource implements IdlingResource {
private final Context context;
private ResourceCallback resourceCallback;
public static boolean status = false;
public IntentServiceIdlingResource(Context context) {
this.context = context;
}
#Override
public String getName() {
return IntentServiceIdlingResource.class.getName();
}
#Override
public boolean isIdleNow() {
return getTimer();
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
private static boolean getTimer() {
new CountDownTimer(5000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// Do Nothing
status = false;
}
#Override
public void onFinish() {
status = true;
}
};
return status;
}
}
Exception:
android.support.test.espresso.IdlingResourceTimeoutException: Wait for [com.adbs.recorder.IntentServiceIdlingResource] to become idle timed out
You need an IdlingResource with an isIdleNow() that returns true only if the specific amount of time has passed. To achieve that, save the start time and compare it with current time:
public class ElapsedTimeIdlingResource implements IdlingResource {
private final long startTime;
private final long waitingTime;
private ResourceCallback resourceCallback;
public ElapsedTimeIdlingResource(long waitingTime) {
this.startTime = System.currentTimeMillis();
this.waitingTime = waitingTime;
}
#Override
public String getName() {
return ElapsedTimeIdlingResource.class.getName() + ":" + waitingTime;
}
#Override
public boolean isIdleNow() {
long elapsed = System.currentTimeMillis() - startTime;
boolean idle = (elapsed >= waitingTime);
if (idle) {
resourceCallback.onTransitionToIdle();
}
return idle;
}
#Override
public void registerIdleTransitionCallback(
ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}
Create and register this idling resource in your test:
#Test
public static void waitForOneHour() {
long waitingTime = DateUtils.HOUR_IN_MILLIS;
// Start
onView(withId(AaEspressoTest.getId("recorderpage_record")))
.perform(click());
// Make sure Espresso does not time out
IdlingPolicies.setMasterPolicyTimeout(
waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(
waitingTime * 2, TimeUnit.MILLISECONDS);
// Now we wait
IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);
// Stop
onView(withId(AaEspressoTest.getId("recorderpage_stop")))
.perform(click());
// Clean up
Espresso.unregisterIdlingResources(idlingResource);
}
You need the setMasterPolicyTimeout and setIdlingResourceTimeout calls to make sure Espresso does not terminate the test due to time out.
Full example: https://github.com/chiuki/espresso-samples/tree/master/idling-resource-elapsed-time
The default timeout that Espresso will wait for all registered resources to become idle is one minute.
You can change this using the IdlingPolicies class to set an explicit timeout:
IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.HOURS);
#Before
public void registerIdlingResource() {
IdlingPolicies.setMasterPolicyTimeout(60 * 1000 * 3, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(60 * 1000 * 3, TimeUnit.MILLISECONDS);
mIdlingResource = BooleanIdlingResource.getIdlingResource();
// To prove that the test fails, omit this call:
IdlingRegistry.getInstance().register(mIdlingResource);
}
I test on my project, It works.
Just setup before register idling resources.
please check:
https://github.com/googlesamples/android-testing/tree/master/ui/espresso/IdlingResourceSample
and
https://developer.android.com/reference/android/support/test/espresso/IdlingPolicies

Using Espresso idling resource with multiple activities

I have a firstActivity that launches the secondActivity, where in the secondActivity I have a loading Dialog (not AsyncTask), and I need to make Espresso wait until the dialog disappears before it continues with the test.
Where do I have to implement the IdlingResource? How can I make it wait for the dismissDialog() function?
Here is what I've tried to do:
class DocumentLoadingIdlingResource implements IdlingResource {
private ResourceCallback callback;
#Override
public String getName() {
return "Documnet loading idling resource";
}
#Override
public boolean isIdleNow() {
Activity activity;
try {
activity = getCurrentActivity();
} catch (Throwable e) {
return false;
}
if(activity.getClass().getName().equals(EditorActivity.class.getName())
&& activity.loadingDialogShowing() == false) {
return false;
}
return true;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}
}
Activity getCurrentActivity() throws Throwable {
getInstrumentation().waitForIdleSync();
final Activity[] activity = new Activity[1];
runTestOnUiThread(new Runnable() {
#Override
public void run() {
java.util.Collection<Activity> activites = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
activity[0] = com.google.common.collect.Iterables.getOnlyElement(activites);
}});
return activity[0];
}
This class is implemented in the test class.
There are a few problems here:
Your isIdleNow() calls getCurrentActivity() which calls waitForIdleSync() and runTestOnUiThread(). isIdleNow Javadoc says: "Espresso will always call this method from the main thread, therefore it should be non-blocking and return immediately." So this won't work as is, but you could call getActivitiesInStage directly from isIdleNow.
Your other issue is that you store the reference to ResourceCallback but never invoke onTransitionToIdle, also you should allow for the possibility of more than one ResourceCallback being registered and call onTransitionToIdle on all of the callbacks.
You can do the following:
Copy/Paste IdlingResource into your app as com.mycompany.IdlingResource.
Then have your Activity implement that interface and make sure to call onTransitionToIdle when the dialog goes away and make sure isIdleNow returns false iff the dialog is showing.
In your test code, write a "IdlingResourceAdapter" that wraps com.mycompany.IdlingResource and turns it into an Espresso IdlingResource and register that with Espresso.
This will be simpler once this issue is implemented: https://code.google.com/p/android-test-kit/issues/detail?id=71
I stumbled upon this question in my search for a similar answer. Using concepts from Stefano Dacchille's article on IdlingResources, I built the following idling resource that waits for a specific Activity to be active before firing. In my case, I know the dialog is showing when a fragment with a specific tag exists. This isn't the same as the OP's test, but the concepts should translate well.
public class BusyWhenFragmentExistsInActivityIdlingResource implements IdlingResource {
private FragmentActivity activity = null;
private final String fragmentTag;
private ResourceCallback resourceCallback;
private boolean wasIdleLastTime = true; // Start off as idle
private final String name;
// Need this strong reference because ActivityLifecycleMonitorRegistry won't hold one
private final ActivityLifecycleCallback activityLifecycleCallback;
public BusyWhenFragmentExistsInActivityIdlingResource(
final Class<? extends FragmentActivity> clazz,
final String fragmentTag
){
name = BusyWhenFragmentExistsInActivityIdlingResource.class.getSimpleName()+" "+clazz.getSimpleName();
this.fragmentTag = fragmentTag;
activityLifecycleCallback = new ActivityLifecycleCallback() {
#Override
public void onActivityLifecycleChanged(Activity activity, Stage stage) {
if (!FragmentActivity.class.isAssignableFrom(activity.getClass())) {
return;
}
FragmentActivity fragmentActivity = (FragmentActivity) activity;
if (!clazz.isAssignableFrom(fragmentActivity.getClass())) {
return;
}
switch (stage){
case RESUMED:
BusyWhenFragmentExistsInActivityIdlingResource.this.activity = fragmentActivity;
break;
case STOPPED:
BusyWhenFragmentExistsInActivityIdlingResource.this.activity = null;
break;
}
}
};
ActivityLifecycleMonitorRegistry.getInstance()
.addLifecycleCallback(activityLifecycleCallback);
}
#Override
public String getName() {
return name;
}
#Override
public boolean isIdleNow() {
if (activity==null) {
return wasIdleLastTime = true;
}
boolean isIdleThisTime = activity
.getSupportFragmentManager()
.findFragmentByTag(fragmentTag)==null;
if (!wasIdleLastTime && isIdleThisTime && resourceCallback!=null){
resourceCallback.onTransitionToIdle();
}
return wasIdleLastTime = isIdleThisTime;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}
To use it, add something similar to this to your test:
#Before
public void setUp() throws Exception {
registerIdlingResources(new BusyWhenFragmentExistsInActivityIdlingResource(
SomeOtherActivity.class,
BaseActivity.LOADING_DIALOG
));
}

Categories

Resources