Android Instrumentation Testing - UI Thread Issues

android instrumentation test example
uithreadtest
android ui testing
android ui thread
android run on ui thread
android thread example
ui thread test
android test button click

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!

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.

Android Instrumentation Testing - UI Thread Issues, Espresso detects when the main thread is idle, so it is able to run your test The Espresso testing framework is an instrumentation-based API and works with the� By default, instrumentation tests are most suited for checking values of UI components when an activity is run. However, as I mentioned earlier, instrumentation tests work by using a test app to

You can run portion of your test on the main UI thread with the help of UiThreadTestRule.runOnUiThread(Runnable):

@Rule
public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();

@Test
public void loadWorkOrder_displaysCorrectly() throws Exception {
    final WorkOrderDetails activity = activityRule.getActivity();

    uiThreadTestRule.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            WorkOrder workOrder = new WorkOrder();
            activity.updateDetails(workOrder);
        }
    });

    //Verify customer info is displayed
    onView(withId(R.id.customer_name))
            .check(matches(withText("John Smith")));
}

In most cases it is simpler to annotate the test method with UiThreadTest, however, it may incur other errors such as java.lang.IllegalStateException: Method cannot be called on the main application thread (on: main).

FYR, here is a quote from UiThreadTest's Javadoc:

Note, due to current JUnit limitation, methods annotated with Before and After will also be executed on the UI Thread. Consider using runOnUiThread(Runnable) if this is an issue.

Please note UiThreadTest (package android.support.test.annotation) mentioned above is different from (UiThreadTest (package android.test)).

Test UI for a single app, The main thread has a very simple design: Its only job is to take and execute blocks of work from a thread-safe work queue until its app is� By design, Android View objects are not thread-safe. An app is expected to create, use, and destroy UI objects, all on the main thread. If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.

The accepted answer is now deprecated

The easiest way to achieve this is simply using UiThreadTest

import android.support.test.annotation.UiThreadTest;

        @Test
        @UiThreadTest
        public void myTest() {
            // Set up conditions for test

            // Call the tested method
            activity.doSomethingWithAView()

            // Verify that the results are correct
        }

Better performance through threading, Optional -- UI testing with UI Automator specify AndroidJUnitRunner as the default test instrumentation runner in your project by including the� Just for the record, currently Android Instrumented Tests don't work. Just including runner for junit5 breaks building test apk with different exceptions about using Java 8 features or java.lang.TypeNotPresentException: Type java.lang.module.ModuleReference not present.

With the androidx test runner a new class was added UiThreadStatement that gives a runOnUiThread method for this.

        UiThreadStatement.runOnUiThread {
           // call activity here 
        }

Build instrumented unit tests, The instrumentation thread, where most of your tests run. If you need a test to execute on the main thread, annotate it using @UiThreadTest . locate and interact with UI elements in your app in a thread-safe way. To learn� The Espresso testing framework is an instrumentation-based API and works with the AndroidJUnitRunner test runner. Set up Espresso Before building your UI test with Espresso, make sure to set a dependency reference to the Espresso library: dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'}

The accepted answer describes what is going on perfectly.

As an addition, in case someone is curious why Espresso's methods that touch the UI e.g. perform(ViewActions ...) don't need to do the same, it is simply because they end up doing it later for us.

If you follow perform(ViewActions ...) you will find it ends up doing the following (in android.support.test.espresso.ViewInteraction):

private void runSynchronouslyOnUiThread(Runnable action) {
    ...
    mainThreadExecutor.execute(uiTask);
    ...
}

That mainThreadExecutor is itself annotated with @MainThread.

In other words, Espresso also needs to play by the same rules described by David on the accepted answer.

Fundamentals of Testing, However, note that you cannot update the UI from any thread other than the UI thread or the "main" thread. To fix this problem, Android offers� On a real or virtual device, ActivityScenario provides thread safety, synchronizing events between your test's instrumentation thread and the thread that runs your activity under test. The API is also particularly well-suited for evaluating how an activity under test behaves when it's destroyed or created.

Processes and threads overview, Instrumentation tests can be used to test none UI logic as well. way forward, we 'll use espresso to take actions on the main thread such as button odd issues when you switch between Unit Tests and Instrumentation Tests� This document explains tools available to measure UI performance, and lays out an approach to integrate UI performance measurements into your testing practices. Measure UI performance In order to improve performance you first need the ability to measure the performance of your system, and then diagnose and identify problems that may arrive from

The basics of Unit and Instrumentation Testing on Android, For Android, all UI updation is handled by the main thread and if the main Instrumentation Testing : Instrumentation tests are specifically designed to The default animations can cause issues during the Espresso testing� The InstrumentationTestRunner is the base test runner for Android tests. This test runner starts and loads the test methods. Via the instrumentation API it communicates with the Android system. If you start a test for an Android application, the Android system kills any process of the application under test and then loads a new instance.

Testing in Android with Espresso, To gain access to the instrumentation API and build tests at this level, Android Advancing to a higher level in the pyramid, we have UI testing with Espresso. that are not very well managed by the test tools; Thread Concurrency Issues; And � It is a native testing framework, designed specifically for Android native app instrumentation tests; It has perfect synchronisation between the tests and the IDLE (UI thread) of the Android platform;

Comments
  • The runOnUiThread approach worked when combined with getInstrumentation().waitForIdleSync();. The @Before approach didn't work unfortunately. Thanks for the help!
  • ^ this is important when you have an assertion immediately after, otherwise you can get a race condition with inconsistent test results!
  • If future visitors stumble upon Android.Test.UIThreadTest is deprecated as of API 24: Android 7.0 (Nougat), note that this is not the same annotation. This answer is using the support annotation. That cannot be resolved for me though
  • This answer worked for me on Nougat
  • In myTest where did activity come from? That's what I'm stuck on.