Reputation: 780
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
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