Code
Ultron

Ultron is an easiest framework to develop Android UI tests. It makes your tests simple, stable and supportable. It’s based on Espresso, UI Automator and Compose UI testing framework. Ultron provides a lot of new great features. Ultron also gives you a full control under your tests!
Moreover, you don’t need to learn any new classes or special syntax. All magic actions and assertions are provided from crunch. Ultron can be easially customised and extended. Wish you only stable tests!
What are the benefits of using the framework?
- Simple and presentative syntax
- Stability of all actions and assertions
- Full control under any action or assertion
- An architectural approach to UI tests development
- Amazing mechanism of setups and teardowns (You even can setup preconditions for single test in test class. It won’t affect the others)
- Great Compose support
- Allure report generation out of the box
A few words about syntax
The standard Google syntax is complex and not intuitive to understand. This is especially evident when interacting with the RecyclerView
Let’s look at some examples:
here)
1. Simple compose operation (read wikiCompose framework
composeTestRule.onNode(hasTestTag("Continue")).performClick()
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()
Ultron
hasTestTag("Continue").click()
hasText("Welcome").assertIsDisplayed()
here)
2. Compose list operation (read wikiCompose framework
val itemMatcher = hasText(contact.name)
composeRule
.onNodeWithTag(contactsListTestTag)
.performScrollToNode(itemMatcher)
.onChildren()
.filterToOne(itemMatcher)
.assertTextContains(contact.name)
Ultron
composeList(hasTestTag(contactsListTestTag))
.item(hasText(contact.name))
.assertTextContains(contact.name)
3. Simple Espresso assertion and action.
Espresso
onView(withId(R.id.send_button)).check(isDisplayed()).perform(click())
Ultron
withId(R.id.send_button).isDisplayed().click()
It looks better. Names of all Ultron operations are the same as Espresso. It also provide a list of additional operations.
See wiki for more info.
4. Action on RecyclerView list item
Espresso
onView(withId(R.id.recycler_friends))
.perform(
RecyclerViewActions
.scrollTo<RecyclerView.ViewHolder>(hasDescendant(withText("Janice")))
)
.perform(
RecyclerViewActions
.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant(withText("Janice")),
click()
)
)
Ultron
withRecyclerView(R.id.recycler_friends)
.item(hasDescendant(withText("Janice")))
.click()
Read wiki and realise the magic of how Ultron interacts with RecyclerView.
5. Espresso WebView operations
Espresso
onWebView()
.withElement(findElement(Locator.ID, "text_input"))
.perform(webKeys(newTitle))
.withElement(findElement(Locator.ID, "button1"))
.perform(webClick())
.withElement(findElement(Locator.ID, "title"))
.check(webMatches(getText(), containsString(newTitle)))
Ultron
id("text_input").webKeys(newTitle)
id("button1").webClick()
id("title").hasText(newTitle)
Read wiki
6. UI Automator operations
UI Automator
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device
.findObject(By.res("com.atiurin.sampleapp:id", "button1"))
.click()
Ultron
byResId(R.id.button1).click()
Read wiki
You can get the result of any operation as boolean value
val isButtonDisplayed = withId(R.id.button).isSuccess { isDisplayed() }
if (isButtonDisplayed) {
//do some reasonable actions
}
Why all Ultron actions and assertions are much more stable?
The framework catches a list of specified exceptions and tries to repeat operation during timeout (5 sec by default). Ofcourse, you are able to customise the list of processed exceptions. It is also available to specify custom timeout for any operation.
withId(R.id.result).withTimeout(10_000).hasText("Passed")
3 steps to develop a test using Ultron
We try to advocate the correct construction of the test framework architecture, the division of responsibilities between the layers and other proper things.
Therefore, I would like to recommend the following approach when your are using Ultron.
- Create a PageObject class and specify screen UI elements as
Matcher<View>
objects.
object ChatPage : Page<ChatPage>() {
private val messagesList = withId(R.id.messages_list)
private val clearHistoryBtn = withText("Clear history")
private val inputMessageText = withId(R.id.message_input_text)
private val sendMessageBtn = withId(R.id.send_button)
}
It’s recommended to make all PageObject classes as object
and descendants of Page class. In this case you will be able to use cool kotlin magic.
- Describe user step methods in PageObject class.
object ChatPage : Page<ChatPage>() {
fun sendMessage(text: String) = apply {
inputMessageText.typeText(text)
sendMessageBtn.click()
getMessageListItem(text).text
.isDisplayed()
.hasText(text)
}
fun clearHistory() = apply {
openContextualActionModeOverflowMenu()
clearHistoryBtn.click()
}
}
Full code sample ChatPage.class
- Call user steps in test
@Test
fun friendsItemCheck(){
FriendsListPage {
assertName("Janice")
assertStatus("Janice","Oh. My. God")
}
}
@Test
fun sendMessage(){
FriendsListPage.openChat("Janice")
ChatPage {
clearHistory()
sendMessage("test message")
}
}
Full code sample DemoEspressoTest.class
In general, it all comes down to the fact that the architecture of your project will look like this.
Allure report
Ultron can generate artifacts for Allure report. Just apply recommended config and set testIntrumentationRunner.
Read full guide in wiki
@BeforeClass @JvmStatic
fun setConfig() {
UltronConfig.applyRecommended()
UltronAllureConfig.applyRecommended()
}
for Compose add 3 lines more
@BeforeClass @JvmStatic
fun setConfig() {
...
UltronComposeConfig.applyRecommended()
UltronComposeConfig.addListener(ScreenshotAttachListener())
UltronComposeConfig.addListener(DetailedOperationAllureListener())
}
Add Ultron to your project
Gradle
repositories {
mavenCentral()
}
dependencies {
androidTestImplementation 'com.atiurin:ultron:<latest_version>'
androidTestImplementation 'com.atiurin:ultron-allure:<latest_version>'
androidTestImplementation 'com.atiurin:ultron-compose:<latest_version>'
}
AndroidX
It is required to use AndroidX libraries. You can get some problems with Android Support ones.
