Android test with Espresso IdlingResource - android

I'm trying to test that the AutoCompleteTextView will show the items after some word will be typed. But there is a delay between typing and showing the popup. First i was using Thread.sleep() and it was working just fine. But I know that this approach isn't clear so I'm trying to accomplish it with IdlingResource. But it doesn't work for me. I literally read first 5 pages of Google responses, but either I don't understand how it should work, or I have some error in my code.
Here is the code:
static class AutocompleteShowIdlingResource implements IdlingResource {
private Activity activity;
private #IdRes int resId;
private ResourceCallback resourceCallback;
public AutocompleteShowIdlingResource(Activity activity, #IdRes int resId) {
this.activity = activity;
this.resId = resId;
}
#Override
public String getName() {
return this.getClass().getName() + resId;
}
#Override
public boolean isIdleNow() {
boolean idle = ((AutoCompleteTextView) activity.findViewById(resId)).getAdapter() != null;
Log.d(TAG, "isIdleNow: " + idle);
if (idle) {
resourceCallback.onTransitionToIdle();
}
return idle;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.resourceCallback = callback;
}
}
The test itself:
Activity activity = calibrationActivityRule.getActivity();
onView(withId(R.id.autocomplete_occupation)).perform(typeText("dok"));
IdlingResource idlingResource = new AutocompleteShowIdlingResource(activity, R.id.autocomplete_occupation);
Espresso.registerIdlingResources(idlingResource);
assertEquals(((AutoCompleteTextView) activity.findViewById(R.id.autocomplete_occupation)).getAdapter().getCount(), 3);
Espresso.unregisterIdlingResources(idlingResource);
But the test fails on java.lang.NullPointerException when trying to call getCount() on null adapter. The log is printing
isIdleNow: false
just once, which is quite strange.
There isn't much clear examples how to use IdlingResource, so maybe someone can make it clear for me. Thanks.

Your IdlingResource will have an effect only if you use it together with onView(...).check(...) or onData(...).check(...). Actually, the "magic" will happen in the check call - it's the place where Espresso waits until there are no running AsyncTasks or no blocking IdlingResources.
Now let's correct your code so that it works:
Activity activity = calibrationActivityRule.getActivity();
onView(withId(R.id.autocomplete_occupation)).perform(typeText("dok"));
IdlingResource idlingResource = new AutocompleteShowIdlingResource(activity, R.id.autocomplete_occupation);
try {
Espresso.registerIdlingResources(idlingResource);
//that's where Espresso will wait until the idling resource is idle
onData(anything()).inAdapter(withId(R.id.autocomplete_occupation)).check(matches(isDisplayed());
finally {
Espresso.unregisterIdlingResources(idlingResource);
}
assertEquals(((AutoCompleteTextView) activity.findViewById(R.id.autocomplete_occupation)).getAdapter().getCount(), 3);

Related

Check whether an activity has finished loading all data without changing application code

I am trying to find a way to verify that an activity has finished loading everything only without doing any changes in the application code. The method mentioned in this question and many others requires some application code change and I would like to do it via androidTest section.
There are scenarios when the activity is not fully loaded and running the following code fails:
onView(allOf(withId(R.id.user_name), withText("username1"))).perform(click());
In this example I am waiting for a ListView to load, so the data may also be loaded asynchronously (I am using espresso).
Might be too late but you should take a look at espresso idling resource to sync your background loading tasks with espresso. You wont need to change any of your application code. Here you have a deeper insight on android custom idling resources: http://dev.jimdo.com/2014/05/09/wait-for-it-a-deep-dive-into-espresso-s-idling-resources/
or this http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html
Here is what I did to make espresso wait for my list to be populated (from data comming from the network) before running the UI test.
public class ListResultsIdlingResource implements IdlingResource {
private ResourceCallback callback;
private RecyclerView mRecyclerListings;
public ListResultsIdlingResource(RecyclerView recyclerListings) {
mRecyclerListings = recyclerListings;
}
#Override
public boolean isIdleNow() {
if (mRecyclerListings != null && mRecyclerListings.getAdapter().getItemCount() > 0) {
if (callback != null) {
callback.onTransitionToIdle();
}
return true;
}
return false;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}
#Override
public String getName() {
return "Recycler idling resource";
}
You just have to check that your list has items in you isIdleNow() method before running your UI test over its elements.
And in your espresso test class in your setup method register your idling resource and pass it a reference to your ListView or Recyclerview or any view you are using as list.
#Before
public void setUp() throws Exception {
Intent intent = createIntentForActivity();
activityRule.launchActivity(intent);
mActivity = activityRule.getActivity();
mListResultsIdlingResource = new ListingResultsIdlingResource(
(RecyclerView) mActivity.findViewById(R.id.recycler_view));
registerIdlingResources(mListResultsIdlingResource);
}
Hope this is helpful for anyone looking for this.

Wait until view become visible with IdleResource

I am writing Instrumentation tests with usage of Espresso 2.2.
Flow I want to test:
radioButton clicked by test
onClick launches request to API
after every time different time I receive response
positive response triggers interface method that is called in activity
onRequestSuccess I am making additional panel on screen named vSetupAmount visible
I want to register IdleResource after click on radioButton so it waits until vSetupAmount becomes visible. But I can't make it work. Please tell me what am I doing wrong.
I have written such IdleResource:
public class AmountViewIdlingResource implements IdlingResource {
private CountriesAndRatesActivity activity;
private ResourceCallback callback;
private SetupAmountView amountView;
public AmountViewIdlingResource(CountriesAndRatesActivity activity) {
this.activity = activity;
amountView = (SetupAmountView) this.activity.findViewById(R.id.vSetupAmount);
}
#Override public String getName() {
return "Amount View idling resource";
}
#Override public boolean isIdleNow() {
callback.onTransitionToIdle();
return amountView.getVisibility() == View.VISIBLE;
}
#Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.callback = resourceCallback;
}
}
So I am passing activity to IdleResource, link view with variable. I understand that IdleResource won't let test go through until isIdleNow() returns value true. So if view is View.GONE then it won't go further.
How it looks in test:
// click of radioButton picked from radioGroup
onView(withId(rgDeliveries.getChildAt(id).getId())).perform(scrollTo(), click());
// wait for view to become visible
Espresso.registerIdlingResources(new AmountViewIdlingResource(getActivity()));
// go to button on view
onView(withId(R.id.btnGetStarted)).perform(scrollTo());
// unregister idle resource
for ( IdlingResource resource : getIdlingResources()) {
Espresso.unregisterIdlingResources(resource);
}
So I get my click on radioButton. IdleResource is successfully registered but nothing happens. On my device API response comes. vSetupAmount is being displayed but
amountView.getVisibility() == View.VISIBLE;
which is being checked forever (but I see my view on screen) always returns false.
What am I doing wrong?
I know this might be too late, but my answer might help someone.
When you instantiate and register your AmountViewIdlingResource, keep a reference to it and only unregister that reference, don't unregister all IdlingResources.
Your implementation of the isIdleNow() method should only call the callback's onTransitionToIdle() method if your idling resource is really idle not every time.
I needed to do something similar, to wait for a button to appear on the screen, which in turn only got to be visible after an HTTP call finished. My implementation of a view visibility Idling Resource is as follows:
public class ViewVisibilityIdlingResource implements IdlingResource {
private final View mView;
private final int mExpectedVisibility;
private boolean mIdle;
private ResourceCallback mResourceCallback;
public ViewVisibilityIdlingResource(final View view, final int expectedVisibility) {
this.mView = view;
this.mExpectedVisibility = expectedVisibility;
this.mIdle = false;
this.mResourceCallback = null;
}
#Override
public final String getName() {
return ViewVisibilityIdlingResource.class.getSimpleName();
}
#Override
public final boolean isIdleNow() {
mIdle = mIdle || mView.getVisibility() == mExpectedVisibility;
if (mIdle) {
if (mResourceCallback != null) {
mResourceCallback.onTransitionToIdle();
}
}
return mIdle;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
mResourceCallback = resourceCallback;
}
}
Hope this helps

espresso test ignores IdlingResouce isIdle() returning false

There is an IdlingResource, e.g. like this
public class IRWatchlistNamesLoaded implements IdlingResource {
private final static String CLASSNAME = "IRWatchlistNamesLoaded";
private ResourceCallback callback;
public IRWatchlistNamesLoaded () {}
#Override
public String getName() {
return getClass().getName();
}
#Override
public boolean isIdleNow() {
if(LMaxApplication.watchlists.getNames() != null && LMaxApplication.watchlists.getNames().size() > 0) {
callback.onTransitionToIdle();
CustomLog.print(CustomLog.UI_TEST, CLASSNAME, "isIdleNow = TRUE. size = " + LMaxApplication.watchlists.getNames().size());
return true;
}
CustomLog.print(CustomLog.UI_TEST, CLASSNAME, "isIdleNow = FALSE. size = " + LMaxApplication.watchlists.getNames().size());
return false;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.callback = resourceCallback;
}
}
And the usage is standard - when I need the resource I call
watchlistLoadedIR = new IRWatchlistNamesLoaded();
needToUnregisterWatchlistLoadedIR = true;
Espresso.registerIdlingResources(watchlistLoadedIR);
What I see in logs is that isIdle() returns false (1 or 2 times), my test keeps going and my resource is not loaded properly, so test fails. Also, need to notice that in some other tests this IdlingResource works and espresso really waits for the resource to be idle. The usage is absolutely the same.
Please, maybe somebody has any idea why this could happen?
I don't know if this will help but there is a great way of setting up IdlingResources described here which might help solve your problem. I have a sneaking suspicion that it has to do with the fact that you are instantiating your class within the test, which I don't think is what you want to be doing, since you need to be using the IdlingResources from with an activity, using onView to perform action on different components which would then end up running the class in question.

Android Espresso: Wait for Activity to finish/start

Is there a canonical solution using Espresso to wait for a specific Activity to finish or start?
I have a SplashActivity that appears for a few seconds, then a MainActivity. I want Espresso to interact with the MainActivity, not the SplashActivity, but I can't seem to find any information about waiting for such a condition.
The closest thing I can find is a mention of idle resources but its not clear to me how I would use that here to wait for the Activity.
I guess your splash activity is performing some initialization.
If this is the case, my suggestion is to define some sort of listener pattern in order to be able to get a callback when the initialization is done. Then, you can make Espresso wait for the initialization with an IdlingResource.
NB: The following is NOT complete code, but it is meant to give you a hint in how to do so:
public class SplashIdlingResource implements IdlingResource, YourApplicationInitListener {
// volatile because can be set by a different
// thread than the test runner: the one calling back
private volatile boolean mIsInitialized;
private ResourceCallback mCallback;
public SplashIdlingResource() {
YourApplication application = // retrieve your Application object
mIsInitialized = application.isInitialized();
if (!mIsInitialized) {
application.addInitListener(this);
}
}
#Override
public String getName() {
return SplashIdlingResource.class.getName();
}
#Override
public boolean isIdleNow() {
return mIsInitialized;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
#Override
public void onApplicationInitCompleted() {
m_isInitialized = true;
if (m_callback != null) {
m_callback.onTransitionToIdle();
}
}
}
Where onApplicationInitCompleted() is the callback you defined and which must be called when the Splash Activity, and so the initialization, is done.
Finally, register this new IdlingResource with Espresso by calling Espresso.registerIdlingResource in test setup.

Converting class that creates threads into Service

I have this class that creates threads, but I want to convert that class into Service hoping it wont be destroyed on orientation change.
Here is the class:
public class Getter {
private final String ip;
private final int amount, poolSize;
private Vector<Integer> results = new Vector<Integer>();
private final ExecutorService es;
private Collection<Future<?>> futures = new LinkedList<Future<?>>();
public Getter(String ip, int amount, int poolSize) {
this.ip = ip;
this.amount = amount;
this.poolSize = poolSize;
es = Executors.newFixedThreadPool(this.poolSize);
}
public boolean working() {
boolean work = false;
for (Future<?> future : futures) {
if (!future.isDone()) {
work = true;
}
}
return work;
}
public Vector<Integer> getResults() {
Collections.sort(results);
return results;
}
public int threads(){
return poolSize;
}
public void start() {
for (int i = 0; i <= amount; i++) {
futures.add(es.submit(new Get(ip)));
}
es.shutdown();
}
public void stop(){
for (Future<?> future : futures) {
future.cancel(true);
}
}
private class Get implements Runnable {
private String ip;
private Get(String ip) {
this.ip = ip;
}
public void run() {
try {
// network stuff
// adds result to results Vector.
} catch (Exception ex) {
}
}
}
}
so is this class possible to convert into Service so it would run on background no matter what, once its started?
you can add this to a manifest file that stops your app getting destroyed on orientation change:
android:configChanges="orientation"
but if you want to make a service then just copy this example:
http://developer.android.com/reference/android/app/Service.html
you could add the class to the service and then just add an accessor to your service connection.
You can also add something to the manifest file to let your service start when the device is turned on
see here
Trying to start a service on boot on Android
If you want a Service which runs "no matter what", you might want to set it as foreground.
Yes. I think this would make a good IntentService. Your next-best choice is probably AsyncTask.
More:
Generally, I would say, if the behavior of the background task is closely tied to whatever starts it (e.g., an Activity, Fragment, Service, whatever), then make it an AsyncTask. If it is self-contained and meant to serve several different components in a modular fashion, make it a Service. In either case, an AsyncTask or IntentService is very easy to make.

Categories

Resources