I am implementing a testcase in Android Juint. I am facing problem in
handling threads. After the successful running of a test case, it will
not wait for child thread to be finished. e.g. If one testcase call
some services in server. testcase will successfully send request to a
server, but it will not wait for a response. Because testcase will
start this request in a different Thread. Currently I am using
following code,
currentThread = Thread.currentThread();
synchronized (Thread.currentThread()) {
try {
Thread.currentThread().wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
After getting response I am using the following code to start the
current thread again.
currentThread.interrupt();
I think this is not the good way to do it. There must be a some other
technique to handle this problem properly. Please let me know as soon
as possible if any one knows solution for this.
Thanks,
Vashishta
In your code the synchronized block never blocks (wait for another thread), because you synchronize on Thread.currentThread() which is a different object in different threads.
You have so synchronize on an object common to all threads: typically an object that contains shared data. Then you synchronize{..} a block od code that manipulates this data.
Related
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!!)
I get a error of NetworkOnMainThreadException in the line of AVObject brandObj = query.getFirst(); After did some searches about the error, I think I should use something like Asynctask. But couldn't figure out how to do it.
AVObject is same as ParseObject
public class Product {
Product(AVObject object) {
try {
AVObject brandObj = query.getFirst(); // this one is making network request
} catch (AVException e) {
e.printStackTrace();
}
}
}
Should I extends AsyncTask<AVObject, Void, AVObject>
Then
#Override
protected AVObject doInBackground(AVObject... objects) {
return null;
}
But not sure what should I write in doInBackground
Any suggestions?
NetworkOnMainThreadException is thrown when an application attempts to
perform a networking operation on its main thread. This is only thrown
for applications targeting the Honeycomb SDK or higher.
You cannot perform any networking on your main thread because it may block the UI. Hence, you should do everything in a separate thread.
AsyncTask is one of the easier solutions. Setup your AsyncTask (follow some tutorials/guides) and put your networking code into doInBackground() method, which runs on a separate thread.
Ideally, an object's constructor does not perform disk I/O or network I/O. In your case, it does.
So, every piece of code that is calling new Product(...) needs to be put into a background thread (plain thread, RxJava chain, JobIntentService, AsyncTask, etc.). Product itself would not change.
I am trying to perform a task when android is fail or pass, is there a way to do this, and where should I put my code?
was thinking to do it in tearDown, but is there a way to check whether the testcase is pass or not??
When your test fails - that is an exception which is thrown.
I would suggest you to put the code related to your test case in try/catch block and in catch block do the stuff you want to do on test failure.
In order to mark that test case as failure you can throw the exception at the end of catch block.
e.g:
try
{
solo.....
//do your stuff
}
catch(Throwable e)
{
//Do what you want to do on test failure.
throw e;
}
You will need to register a test listener to get the pass or fail stance automatically, this however is a little bit of a pain, you will either need to override your instrumentation test runner, or use reflection to get the AndroidTestRunner from your current instrumentation test runner and then you can add in the listener as seen here http://developer.android.com/reference/android/test/AndroidTestRunner.html
There are other ways too but they are all a little bit of manual work (such as catching assertion exceptions within test methods and doing failure case that way.
looks like I've found the solution.
we can override the runTest() method, and do this:
super.runTest():
doSomething()
doSomething() will be executed when test passed..
I am having trouble working with asynctasks. The problem is that I need data from one asynctask to be loaded into a static variable before another asynctask begins since asynctask2 needs that static variable. I tried using this code to stall asynctask2 but it doesn't even show up in my log cat:
//geocode is the first asynctask
while(geocode.getStatus().equals(android.os.AsyncTask.Status.PENDING))
{try {;
wait(100);
Log.i("waiting", "waiting");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
use the onPostExecute of the first AsyncTask to start the next one, that way you know it has finished and it is safe to start the next one. I would however try to think of a better way to accomplish what you are trying to do
With reference to the following code.
for (Future<Long> future : list) {
try {
sum += future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
Now when i call future.get will this be blocking call? So if i am executing this on the Main Thread of and android application will this cause an ANR exception ? I notice the Interrupted exception is also thrown, so does this mean their is a sleep called on the thread in the get function ?
Yes, documentation of Future.get() says:
Waits if necessary for the computation to complete, and then retrieves its result.
so, it will block until results of computation are available, or the computation was interrupted (cancelled or resulting in exception).
It may cause ANR if your computations take long time to finish.
Again, from documentation of Future.get():
#throws InterruptedException if the current thread was interrupted while waiting
Now, since you call Future.get() from main thread, I suspect that it is the Android system which interrupts your main thread in an attempt to make it responsive again. But it may also be caused by another thread of your application - it's hard to tell, since you haven't specified what your application does, what threads does it create, when, do you use ExecutorService (and if so, which implementation), and so on.
No, FutureTask.get() (which I believe is the implementation of Future you're using) does not call Thread.sleep(). Instead, putting the thread in waiting mode is done via thread parking which causes it to not be scheduled for execution until a signal is given. This may cause in ANR error if done in the main thread. For details of parking mechanism consider reading documentation and this question
You can simply check if future "is done" by calling future.isDone(). Code example:
String result = "";
if (myFuture.isDone()) result = (String) myFuture.get();
You will probably want to check it in an endless loop.
Beware of deadlock with future.get(). If both the MainThread, that call future.get(), and the WorkerThread is synchronized (on the same object) then you get a deadlock.