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!
runOnUiThread. Helper method for running part of a method on the UI thread.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With