Danny
Danny

Reputation: 780

Android UI tests occasionally fail due to Robotium being unable to find ImageButton objects

I am trying to debug an Android UI test that fails around 3% of the time.

Our test class starts out like this:

@RunWith(AndroidJUnit4.class)
public class ActionWidgetAdapterTest {

    private Solo solo;

    @Rule
    public ActivityTestRule<SampleContainer> mActivityRule = new ActivityTestRule<>(SampleContainer.class);

    // SampleContainer is used exclusively for the test case and extends AppCompatActivity

    @Before
    public void setUp() throws Exception {
        solo = new Solo(InstrumentationRegistry.getInstrumentation(), mActivityRule.getActivity());
    }

    @After
    public void tearDown() throws Exception {
        solo.finishOpenedActivities();
    }
    // rest of class
    // [...]
}

The problematic test case is as follows:

@Test
@LargeTest
@FlakyTest
public void testAddActions() throws Exception {

    final ArrayList<Action> actions = new ArrayList<>();

    // Action is our in-house version of the Action class from the Leanback library
    final Action a1 = new Action(0, "text1", R.drawable.action_button_focused);
    final Action a2 = new Action(1, "text2", R.drawable.action_button_focused);
    final Action a3 = new Action(0, "text3", R.drawable.action_button_focused);
    final Action a4 = new Action(1, "text4", R.drawable.action_button_focused);

    actions.add(a1);
    actions.add(a2);
    actions.add(a3);
    actions.add(a4);

    // handler for posting to the main thread
    Handler mainHandler = new Handler(mActivityRule.getActivity().getBaseContext()
                                                                 .getMainLooper());

    Runnable myRunnable = () -> {

        // add actions to adapter
        mActivityRule.getActivity().mActionWidgetAdapter.addActions(actions);
    };
    mainHandler.post(myRunnable);

    solo.sleep(1000); // pause to resolve any timing issues
    assertTrue(mActivityRule.getActivity().mActionWidgetAdapter.getItemCount() == 4);

    // test edge case - navigate all the way to the left
    solo.sendKey(Solo.LEFT);
    pressUpDownEnter();
    solo.sendKey(Solo.LEFT);
    pressUpDownEnter();
    solo.sendKey(Solo.LEFT);
    pressUpDownEnter();
    solo.sendKey(Solo.LEFT);
    pressUpDownEnter();
    solo.sendKey(Solo.LEFT);

    assertTrue(solo.getImageButton(0).isFocused());
    assertFalse(solo.getImageButton(2).isFocused());
}

The test case passes the vast majority of the time. However, there is a small chance of failure when assertTrue(solo.getImageButton(0).isFocused()); is executed; Robotium complains that "3 ImageButtons are not found" when this happens. There doesn't seem to be any pattern to this. I upgraded the Robotium framework to the latest version, but that doesn't resolve the issue.

Anyone have an idea what we're doing wrong?

Upvotes: 1

Views: 76

Answers (1)

Danny
Danny

Reputation: 780

I believe I've found the cause. From what I could tell, the issue is due to a race condition in tearDown(). Per the Robotium source code, finishOpenedActivities() works by sending the Back button thrice. However, this appears to be done on a separate thread. As a result, the commands may continue to be sent even as a new test case begins, causing the app being tested to disappear from view and rendering Robotium unable to read the UI.

Considering that the app already gets killed at the end of each test, finishOpenedActivities() would seem kind of redundant. The issue has not come up again after I commented out that line.

Upvotes: 1

Related Questions