Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Instrumentation Testing - UI Thread Issues

I am trying to write an Instrumentation Test for my Android app.

I'm running into some weird threading issues and I can't seem to find a solution.

My Original Test:

@RunWith(AndroidJUnit4.class) public class WorkOrderDetailsTest {      @Rule     public ActivityTestRule<WorkOrderDetails> activityRule = new ActivityTestRule<>(WorkOrderDetails.class);      @Test     public void loadWorkOrder_displaysCorrectly() throws Exception {         final WorkOrderDetails activity = activityRule.getActivity();          WorkOrder workOrder = new WorkOrder();         activity.updateDetails(workOrder);          //Verify customer info is displayed         onView(withId(R.id.customer_name))                 .check(matches(withText("John Smith")));     } } 

This resulted in an

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

...

com.kwtree.kwtree.workorder.WorkOrderDetails.updateDetails(WorkOrderDetails.java:155)

The only thing the updateDetails() method does is some setText() calls.

After researching a bit, it seemed like adding a UiThreadTestRule and android.support.test.annotation.UiThreadTest annotation to my test would fix the problem.

@UiThreadTest:

@RunWith(AndroidJUnit4.class) public class WorkOrderDetailsTest {      //Note: This is new     @Rule     public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();      @Rule     public ActivityTestRule<WorkOrderDetails> activityRule = new ActivityTestRule<>(WorkOrderDetails.class);      @Test     @UiThreadTest //Note: This is new     public void loadWorkOrder_displaysCorrectly() throws Exception {         final WorkOrderDetails activity = activityRule.getActivity();          WorkOrder workOrder = new WorkOrder();         activity.updateDetails(workOrder);          //Verify customer info is displayed         onView(withId(R.id.customer_name))                 .check(matches(withText("John Smith")));     } } 

java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main)

(Note: All of the methods in this stack trace are not my code)

It seems to be giving me mixed results... If it needs to be run on the original thread that created the views but can't run on the main thread, what thread should it be run on?

I'd really appreciate any help or suggestions!

like image 385
Khalos Avatar asked Feb 03 '16 17:02

Khalos


People also ask

Which helper method helps to run a portion of a test in UI thread?

runOnUiThread. Helper method for running part of a method on the UI thread.

What is Android instrumentation testing?

Instrumented tests run on Android devices, whether physical or emulated. As such, they can take advantage of the Android framework APIs. Instrumented tests therefore provide more fidelity than local tests, though they run much more slowly.

Why should you avoid to run non UI code on the main thread in Android?

If you put long running work on the UI thread, you can get ANR errors. If you have multiple threads and put long running work on the non-UI threads, those non-UI threads can't inform the user of what is happening.


1 Answers

Those instrumentation tests run inside their own app. This also means, they run in their own thread.

You must think of your instrumentation as something you install alongside your actual app, so your possible interactions are 'limited'.

You need to call all view methods from the UIThread / main thread of the application, so calling activity.updateDetails(workOrder); from your instrumentation thread is not the application main thread. This is why the exception is thrown.

You can just run the code you need to test on your main thread like you would do if you were calling it inside your app from a different thread by using

activity.runOnUiThread(new Runnable() {     public void run() {         activity.updateDetails(workOrder);     } } 

With this running your test should work.

The illegal state exception you are receiving seems to be because of your interaction with the rule. The documentation states

Note that instrumentation methods may not be used when this annotation is present.

If you start / get your activity in @Before it should also work.

like image 91
David Medenjak Avatar answered Oct 10 '22 11:10

David Medenjak