Testing Dialogs in Robolectric

Brian Terczynski
3 min readDec 12, 2021

When you test a View/Fragment/Activity in Robolectric, you may end up wanting to test whether a Dialog shows up at the right time, verify its contents, and test interactions with the Dialog. For example, you may want to test that a confirmation dialog comes up when you click a “Delete” button. Doing so is pretty easy in Robolectric, via use of the ShadowDialog class.

Let’s say you have the following AlertDialogappear if you press a Delete button:

AlertDialog.Builder(context)
.setMessage("Are you sure you want to delete this item?")
.setPositiveButton("Delete") { _, _ ->
viewModel.delete()
}
.setNegativeButton("Cancel") { _, _ -> }
.show()

In Robolectric, you can test this that it appears, and has the correct message, with the following code:

// Press Delete button
activity.findViewById<View>(R.id.delete).performClick()
// Verify dialog appears with correct content
val dialog = ShadowDialog.getLatestDialog()
assertTrue(dialog.isShowing)
val rootView = dialog.findViewById<View>(android.R.id.message)
assertEquals(
"Are you sure you want to delete this item?",
dialog.findViewById<TextView>(android.R.id.message).text
)

Then to verify the dialog is dismissed, you have to do one extra thing: since the dismiss action is placed on the message queue for the UI thread, the dialog dismissal is not immediate. You have to tell the UI thread to run all of its current tasks to get the dialog to dismiss. So simply clicking on the negative button and then checking the dialog’s visibility right afterwards will not work. Instead, you have to tell Robolectric to run the UI Thread tasks with a call to ShadowLooper.runUiThreadTasks() . So for example:

dialog.findViewById<Button>(android.R.id.button2).performClick()
ShadowLooper.runUiThreadTasks()
assertFalse(dialog.isShowing)

Note in the above that we are testing a standardAlertDialog . But the above also works for any Dialog, including custom Dialogs, custom Dialog Fragments, Bottomsheets, and other dialogs. So if you’re showing your own custom DialogFragment, or showing a Bottomsheet, ShadowDialog.getLatestDialog()will also work.

Using the Espresso APIs to Test Dialogs in Robolectric

One thing to point out about the above: since we are testing the standard Android AlertDialog, to check its contents and find its buttons we are using dialog.findViewById and using standard Android resource IDs (android.R.id.* ) to locate the items of interest. While this will work (for now), it’s relying on some “transparent box” knowledge of the layouts of AlertDialogs , which is probably not the best practice. Because what if Android decided to change the layout and use different resource IDs? A better way to do this would be to use the Espresso APIs with Robolectric. By doing that we can then directly check that the Alert Dialog’s message is shown, and find the button simply by its label.

So to do the above with Robolectric Espresso, you can do the following:

// Click the delete button
onView(withId(R.id.delete)).perform(click())
// Check that the dialog is shown and has valid content.
val dialog = ShadowDialog.getLatestDialog()
assertTrue(dialog.isShowing)
onView(withText("Are you sure you want to delete this item?"))
.inRoot(isDialog())
.check(matches(isDisplayed()))
// Click the cancel button and verify the dialog dismisses
onView(withText("Cancel"))
.inRoot(isDialog())
.perform(click())
ShadowLooper.runUiThreadTasks()
assertFalse(dialog.isShowing)

The key here is the .inRoot(isDialog()) call, because that tells Espresso in which “window” to look for the elements. Otherwise, it will just use whatever the default window is (for example, current Activity) and not actually search the view hierarchy of the Dialog.

As you can see, testing dialogs in Robolectric is pretty easy. Both the classic findViewById approach and the newer Espresso approach can be used. Just remember to tell Robolectric to run through the UI tasks with ShadowLooper.runUiThreadTasks() (or equivalent) if you want to test dialog dismissal.

--

--

Brian Terczynski

Documenting my learnings on my journey as a software engineer.