Is it possible to stop idling for an AsyncTask to complete? - android

I am trying to set up a test for my project - to test that a progress bar is displayed when my app performs a server request.
The code under test uses an AsyncTask to perform the network call.
I have created a blocking server (MockWebServer) to catch and hold the network call - it receives request but doesn't provide a response until i call ".release()". This allows me to verify before the server response occurs.
My logic flows like this:
// Mock server will catch the next network request
BlockingServer blockingServer = createBlockingServer();
// onResume() activity performs network request and shows Progress Spinner
activityTestRule.launchActivity(null);
// onView() waits on UiController.loopUntilIdle() <- Fails here due to timeout.
onView(withId(progressBar)).check(matches(isDisplayed()));
// Tells the server to respond to the network request
blockingServer.release();
onView(withId(progressBar)).check(matches(not(isDisplayed())));
My problem is that because the Code Under Test uses AsyncTask for the server request, Espresso naturally blocks on the verify call (onView()) in order to wait for the AsyncTask to complete before verifying.
What I need is to temporarily stop Espresso idling while waiting for AsyncTask in order to perform the verify while the server is blocking the app logic flow.
(Changing the Code Under Test is not an option)
Can someone help?

So... this is the answer I've arrived at and some working out behind it:
Espresso (specifically calls to onView(), onData(), injectEvent and Actions) uses UiControllerImpl.loopMainThreadUntilIdle() to wait until all "idle-causing" signals are false. It loops over AsyncTask, CompatAsyncTask and something called dynamicIdle to all be idle.
When this method returns the main flow continues.
loopMainThreadUtilIdle() checks an IdleNotifier to check the idle state of each of those three elements. Obviously if you want to stop espresso waiting for AsyncTask the asyncIdle is of particular interest to you.
The IdleNotifier classes are fed into UiControllerImpl at it's construction - this takes place via dagger so you'll need to look at DaggerBaseLayerComponent which uses Providers to grab the construction arguments and pass them into the UiControllerProvider to construct it.
Everything in all of these classes is locked down very tightly. Method and class visibility is usually protected or package-private and final.
The only way I found was to create my own Espresso.java class (onView() and onData()) which used custom DaggerBaseLayerComponent allowing me to use either: My own Providers or My own UiController.
I found however this doesn't solve the whole problem. There is one more mechanism that needs to be coded around - When you're starting activities they use a waitForIdleSync in the Instrumentation class. Usually this is the Runner which is provided in your gradle file. I created my own AndroidJUnitRunner and provided this in gradle to allow me to return from waitForIdleSync on command.
And finally, in startActivitySync in the Instrumentation base class, it uses an array of ActivityWaiter objects to hold up your launchIntent() calls. I couldn't think of a reasonable way of avoiding this so I cheated and created this method in my Runner:
public void clearActivityWaitQueue() {
Object mSync = Whitebox.getInternalState(this, "mSync");
List mWaitingActivities = Whitebox.getInternalState(this, "mWaitingActivities");
if (mSync != null && mWaitingActivities != null) {
mWaitingActivities.clear();
synchronized (mSync) {
mSync.notifyAll();
}
}
}
It uses PowerMock to give me the convenience Whitebox methods to set internal state of Instrumentation:
// Used to give access to Whitebox
androidTestImplementation 'org.powermock:powermock-reflect:1.6.5'
And that's it! Easy right?
(Please tell me it's easier than this and how!!)

Related

How to get WorkManager Status Synchronously

I am working with WorkManager Alpha 05.
I'm developing a Service that enqueues task on demand of other applications.
It has two methods:
createTask (Create a new task, given a name and a set of data, it returns and ID)
checkTaskStatus (The application asks the services given a ID, the status of the task)
The communication is done via bound services using messages. That means both client and services has the correct implementations to communicate information.
Method 1 is working fine.
I have problems with method 2.
WorkManager.getInstance().getStatusById(taskID)
.observe(LifecycleOwner, Observer {
status -> if (status !=null){
val myResult = status.state.toString()
statusString = myResult
Log.d("Task Status",myResult)
}
})
The observer is logging the status correctly, but I can't send back that message to the client. Is there a way to check the status in a sync way?
I don't really need to have the task attached to a LiveData.
Seems like SynchronousWorkManager was removed on October 11:
Removed WorkManager.synchronous() and WorkContinuation.synchronous() and all related methods. Added ListenableFuture as the return type of many methods in the API. This is a breaking API change.
How to use ListenableFuture:
You can now synchronously get and observe by using ListenableFutures. For example, WorkManager.enqueue() used to return void; it now returns a ListenableFuture. You can call ListenableFuture.addListener(Runnable, Executor) or ListenableFuture.get() to run code once the operation is complete.
More info can be found here.
The WorkManager instance has a synchronous method which returns the SynchronousWorkManager, This will give you a set of methods to perform synchronous operations. Take into account that this is meant to be used in a background thread.

Xamarin Android correct Asynchronous way

i am new on xamarin android apps.I want to build an app and i have read that connections with database or the execution of queries should happen asynchronously from the main thread otherwise the UIthread will may collapse.I have found 2 ways to do that:
First way:
WebClient client = new WebClient();
Uri uri = new Uri("http://192.168.2.8/CreateUsername.php");
NameValueCollection parameters = new NameValueCollection();
parameters.Add("Name", txtname.text);
client.UploadValuesCompleted += Client_UploadValuesCompleted;
client.UploadValuesAsync(uri,parameters); ---> is this gonna create a new thread and run asynchronously???
(here i found the first way: https://www.youtube.com/watch?v=jF3D__ibrx8 )
SecondWay:
https://developer.xamarin.com/recipes/android/web_services/consuming_services/call_a_rest_web_service/
Both ways are correct?both of ways are gonna create a new thread and run asynchronously?Thanks!!!!
Both examples are ok but I'd usually trust the official documentation by Xamarin first. What might confuse you with the first example is the lack of await keyword when calling the UploadValuesAsync method. Here's what actually happens:
Call to UploadValuesAsync is made from the UI thread.
UI thread blocks until the method returns because it's running synchronously.
However, the method returns very quickly because it launches the upload process asynchronously on another thread internally and doesn't wait for the process to complete.
When the whole upload process is finished, Client_UploadValuesCompleted will be called.
As you can see on the WebClient.UploadValuesAsync page on MSDN, it states that:
These methods do not block the calling thread.
Also, when looking at the source code for UploadValuesAsync, you'll notice the following attribute defined on top:
[HostProtection(ExternalThreading=true)]
which means the following:
Code that exposes external threading creates or manipulates threads other than its own, which might be harmful to the host.
To sum it up, the upload process is handled on another background thread but otherwise, your code will run synchronously. There's a bit more asynchronicity going on in Xamarin's sample.

Test UI while idling resource is busy

I am new at android testing and I'm running into a problem. I am using RxJava and to test the UI I am using an IdlingResource. While idling resource is busy i cannot test UI.
For example: I have a button. onClick I'm doing a request. While requesting the button disables. After request the button is in enabled state. I want to test the following 3 steps:
Button is enabled before request
Button is disabled while requesting (onCLick)
Button is enabled when requesting ends and response message returns
I would be very very happy if you can help me in this issue...
If you need more information about my issue let me know it. I will edit my post
As I understood, you're trying to test your UI. If so, please, make sure, that you do it in right way:
1). You don't do REAL request.
Please, understand, that your test must always have same behaviour in similar situations. In other words, it must give same result, you're passing same input parameters.
Your input parameters for now:
1.1). Button is enabled before request
1.2). Button disabled during the request
1.3). Buttons enabled after request
As you can see from this list, you don't need to do a real request. It doesn't matter for you, what server will return you (error or success). You even don't need a server for this. All what you need, is just "something", that behaves like a real server. In other words, you have to mock your API client.
I suppose that you're using retrofit. If no, you have to create the interface wrapper for your client. If you're using retrofit, you just need to mock your interface.
Let's suppose, you have next interface:
public interface ApiClient{
#GET("/items")
Observable<MyResponse> doSomeRequest();
}
How do you usually create your API client:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
ApiClient service = retrofit.create(ApiClient.class);
How you should do it in tests:
import static org.mockito.Mockito.*;
and in test method:
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest())
.thenReturn(Observable.just(fakeResponse));
or
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest())
.thenReturn(Observable.defer(new Func0<Observable<MyResponse>>() {
#Override
public Observable<MyResponse> call() {
try{
Thread.sleep(2 * 1000) //2 seconds
}catch(Exception e){
return Observable.error(e);
}
return Observable.just(fakeResponse);
}
}));
P.S. Retrofit adds .subscribeOn(Schedulers.io()) to all Observable's by default. This mocked object doesn't do it. So, please, don't forget to add .subscribeOn(Schedulers.io()) in your code, or apply it to the result of Observable.defer(...)
In code above it will look like:
when(apiMock.doSomeRequest())
.thenReturn(Observable.defer(...).subscribeOn(Schedulers.io()));
And you should pass apiMock to Activity / Fragment which you try to test.
How to do it? See #2.
2). Use DI (dependency injection)
I will not write a lot about it.
I just recomend you to read the documentation on http://google.github.io/dagger/
And especially, how to organise project in way, when you can use real implementaions for production, and mock implementations for testing:
http://google.github.io/dagger/testing.html
In other words, when you're going to build app for usage, you provide real dependencies(in your case it will be real implementation of ApiClient), and when you're going to test some UI or business logic, you pass mock dependencies, which have behaviour specified before the test by you.
This is all, what I wanted to tell you. Hope this helped, and let me know if you have any other questions.
Small addition to Alexander's answer. I would use a Subject for "mocked" api. This allows you to control execution.
//setup your test
Subject<Response,Response> stubResponse = AsyncSubject.create();
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest()).thenReturn(stubResponse.asObservable());
//check first condition that button is enabled before executing action
//click on button
//test your second condition that button is disabled while waiting for response
stubResponse.onNext(fakeResponse); //return fake response
stubResponse.onCompleted();
//test your third condition that button is enabled when you get response back
Remark. Never use sleep in your test. It will slow down your tests and add flakiness.

Android-volley how to unit test passed in Listener

I have some requests I'm making with Android Volley. As the Listeners are doing things like turning the response JSON into objects, I'd like to test them to make sure they're doing the right thing. Thing is, I'm not very caught up on how to do unit testing. I do have Robolectric with JUnit set up, but any help would be appreciated. How would I go about setting up my test so I can test the Listener object passed into the request?
It's enough to look at CacheDispatcher:
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
This is where the request's response is created, using abstract parseNetworkResponse method (in case that you have implemented it), and then:
mDelivery.postResponse(request, response);
which actually fires the listeners, if you dig into the code. Rest of the stuff is thread related. I'd reccomend implementing simple testing routine that takes static NetworkResponse, and calls mDelivery's postResponse.
This actually also means, that you could possibly not go this way - it is enough to test which method (Response.success or Response.error) was called - this is your first unit test. Secondly, just test your listeners.

Fetching big amount of data, what is the best way to go?

I have severals URLs I need to get data from, this should happen in order, one by one. The amount of data returned by requesting those URLs is relatively big. I need to be able to reschedule particular downloads which failed.
What is the best way to go? Shall I use IntentService, Loaders or something else?
Additional note: I would need not only to download, but also post process the data (create tables in db, fill it with data, etc). So DownloadManger can't be of help here.
I would use an IntentService.
It has a number of advantages that are suitable for your needs, including being able to download the data without your application running and supporting automatic restart of the service using setIntentRedelivery().
You can set a number of identifiers for the particular job, you need to perform using Intent extras, and you can keep track of the progress using SharedPreferences - that way you can also resume the work if it's been cancelled previously.
The easiest way is probably to use the system DownloadManager http://developer.android.com/reference/android/app/DownloadManager.html
(answering from my phone, so please excuse the lack of formatting)
I would suggest a service for this. Having service resolves many problems
It would allow reporting of progress asynchronously to the application so you can enable or disable a specific gui in application based on the download status of data
It will allow you to continue the download even if the user switches to other application or closes the application.
Will allow you to establish independent communication with server to prioritize downloads without user interaction.
Try a WakefulIntentService for creating a long-running job that uses wakelocks to keep your task alive and running https://github.com/commonsguy/cwac-wakeful .
Also, if your whole app process is getting killed, you may want to look into persisting the task queue to disk, using something like Tape, from Square
I think the way to go is loading urls in an array, then starting an AsyncTask, returning a boolean to onPostExecute indicating if the operation has success or not. then, keeping a global int index, you can run the AsyncTask with the next index if success, or the same index otherwise. Here is a pseudocode
private int index=0;
//this array must be loaded with urls
private ArrayList<String> urlsArray;
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
class MyDownloaderAsyncTask extends AsyncTask<String,String,Boolean>{
#Override
doInBackground(String... input){
//downlaod my data is the function which download data and return a boolean
return downloadMyData();
}
#Override
onPostExecute(Boolean result){
if(result)
new MyDownloaderAsyncTask().execute(urlsArray.get(++index));
else
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
}
}
hope this help
I have just completed an open source library that can do exactly what you need. Using droidQuery, you can do something like this:
$.ajax(new AjaxOptions().url("http://www.example.com")
.type("GET")
.dataType("JSON")
.context(this)
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
//since dataType is JSON, params[0] is a JSONObject
JSONObject obj = (JSONObject) params[0];
//TODO handle data
//TODO start the next ajax task
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = params[0];
//TODO adjust error.options before retry:
$.ajax(error.request, error.options);
}
}));
You can specify other data types, which will return different object types, such as JSONObject, String, Document, etc.
Similar to #Murtuza Kabul I'd say use a service, but it's a little complicated than that. We have a similar situation related to constant internet access and updates, although ours places greater focus on keeping the service running. I'll try to highlight the main features without drowning you in too much detail (and code is owned by the company ;) )
android.permission.RECEIVE_BOOT_COMPLETED permission and a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED to poke the service awake.
Don't link the service to the Activity, you want it running all the time. eg we call context.startService(new Intent(context.getApplicationContext(), OurService.class))
The service class is just a simple class which registers and calls an OurServiceHandler (as in our case we fire off repeated checks and the Handler manages the 'ticks')
We have an OurServiceRunnable which is a singleton which is checked and called by the Handler for each test. It protects against overlapping updates. It delegates to an OurServiceWorker to do the actual lifting.
Sounds heavy handed, but you want to ensure that the service is always running, always ticking (via the Handler) but only running a single check at a time. You're also going to run into database issue if you use the standard SqlLite DbHelper paradigm, as you can't open the DB on multiple threads and you definitely want the internet access off the main thread. Our hack was a java.util.concurrent.locks.ReentrantLock protecting access to the DB, but you could probably keep DB access on the UI thread and pass DB operations via the Handler.
Beyond this it's just a matter of keeping the downloads atomic in terms of "get task, download task, complete task" or enabling it to pick up from a failed state eg downloaded OK, attempt to complete.
You should take a look at the volley library :
http://www.javacodegeeks.com/2013/06/android-volley-library-example.html
There is also an interesting video of the author that took place at google io 2013 :
http://www.youtube.com/watch?v=yhv8l9F44qo
Mainly because it eases the process of managing a lot of these fastidious tasks that are connection checking, connection interruption, queue management, retry, resume, etc.
Quoting from the javacodegeeks "Advantages of using Volley :
Volley automatically schedule all network requests. It means that Volley will be taking care of all the network requests your app executes for fetching response or image from web.
Volley provides transparent disk and memory caching.
Volley provides powerful cancellation request API. It means that you can cancel a single request or you can set blocks or scopes of requests to cancel.
Volley provides powerful customization abilities.
Volley provides Debugging and tracing tools"
Update from dennisdrew :
For large file, better use a variant of volley which authorize using another http client implementation. This link gives more details :
The volley article about this modification :
http://ogrelab.ikratko.com/android-volley-examples-samples-and-demos/
The github file detail :
https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/toolbox/ExtHttpClientStack.java
public class FetchDataFromDBThread implements Runnable {
/*
* Defines the code to run for this task.
*/
#Override
public void run() {
// Moves the current Thread into the background
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
FetchDataFromDB();
}
}

Categories

Resources