Giter Site home page Giter Site logo

open-tool / ultron Goto Github PK

View Code? Open in Web Editor NEW
80.0 8.0 9.0 3.72 MB

Easiest framework to develop Android UI tests

Home Page: https://t.me/ultron_framework

License: Apache License 2.0

Kotlin 98.01% Java 1.59% HTML 0.35% Shell 0.03% Batchfile 0.03%
ultron espresso uiautomator android uiautomator2 ui-tests architecture espresso-framework kotlin android-ui-tests compose-testing android-compose-tesing android-compose android-compose-ui-test

ultron's Introduction

Ultron

Maven Central Android CI

Ultron is the simplest framework to develop Android UI tests. It provides simplicity, stability, and maintainability to your tests. It's constructed upon the Espresso, UI Automator and Compose UI testing frameworks. Ultron introduces a range of remarkable new features. Furthermore, Ultron puts you in complete control of your tests!

Additionally, 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 exclusively stable tests!

What are the benefits of using the framework?


Wiki

The framework offers an excellent wiki that addresses the majority of significant usage scenarios.

A few words about syntax

The standard syntax provided by Google is intricate and not intuitive. This is especially evident when dealing with RecyclerView interactions.

Let's explore some examples:

1. Simple compose operation (refer to the wiki here)

Compose framework

composeTestRule.onNode(hasTestTag("Continue")).performClick()
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()

Ultron

hasTestTag("Continue").click()
hasText("Welcome").assertIsDisplayed()

2. Compose list operation (refer to the wiki here)

Compose 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()

This presents a cleaner approach. Ultron's operation names mirror Espresso's, while also providing additional operations.

Refer to the wiki for further details.

4. Action on RecyclerView list item

Espresso

onView(withId(R.id.recycler_friends))
    .perform(
        RecyclerViewActions
            .actionOnItem<RecyclerView.ViewHolder>(
                hasDescendant(withText("Janice")),
                click()
            )
        )

Ultron

withRecyclerView(R.id.recycler_friends)
    .item(hasDescendant(withText("Janice")))
    .click()

Explore the wiki to unveil Ultron's magic with RecyclerView interactions.

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)

Refer to the wiki for more details.

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() 

Refer to the wiki


Acquiring the result of any operation as Boolean value

val isButtonDisplayed = withId(R.id.button).isSuccess { isDisplayed() }
if (isButtonDisplayed) {
    //do some reasonable actions
}

Why are all Ultron actions and assertions more stable?

The framework captures a list of specified exceptions and attempts to repeat the operation during a timeout period (default is 5 seconds). Of course, you have the ability to customize the list of handled exceptions. You can also set a custom timeout for any operation.

withId(R.id.result).withTimeout(10_000).hasText("Passed")

3 steps to develop a test using Ultron

We advocate for a proper test framework architecture, division of responsibilities between layers, and other best practices. Therefore, when using Ultron, we recommend the following approach:

  1. Create a Page Object 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 Page Objects as object and descendants of Page class. This allows for the utilization of convenient Kotlin features. It also helps you to keep Page Objects stateless.

  1. Describe user step methods in Page Object.
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()
    }
}

Refer to the full code sample ChatPage.class

  1. 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")
        }
    }

Refer to the full code sample DemoEspressoTest.class

In essence, your project's architecture will look like this:

Allure report

Ultron has built in support to generate artifacts for Allure reports. Just apply the recommended configuration and set testIntrumentationRunner.

For the complete guide, refer to the wiki

@BeforeClass @JvmStatic
fun setConfig() {
    UltronConfig.applyRecommended()
    UltronAllureConfig.applyRecommended()
}

allure

for Compose add 4 lines more

@BeforeClass @JvmStatic
fun setConfig() {
    ...
    UltronComposeConfig.applyRecommended() 
    UltronComposeConfig.addListener(ScreenshotAttachListener())
    UltronComposeConfig.addListener(WindowHierarchyAttachListener())
    UltronComposeConfig.addListener(DetailedOperationAllureListener())
}

allure compose

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>'
}

Please, read gradle dependencies management wiki page.

AndroidX

It is required to use AndroidX libraries. You can get some problems with Android Support ones.

Roadmap

  • #50 Meta information for UI elements
  • #33 Screenshot testign ?

ultron's People

Contributors

alex-tiurin avatar alexandr-stb avatar flotnik avatar itsergpot avatar litemn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ultron's Issues

Ability to setup messages for assertions

Add new function that will allow to setup message for assertion, so when the assertion fails the message will be displayed in logs instead of default one, e.g.
matcher.withUseUnmergedTree(true).withMessage("Element should be enabled").assertIsEnabled()

It would be helpful to have such method not only for compose elements, but also for xml, uiobjects etc

Add 'isListened' boolean parameter for 'isSuccess' function

As for now, when 'isSuccess' returns false, ScreenshotListener will make a screenshot. It's not required in some cases so, if possible, it would be useful to have such switcher and control either should the invoked function be listened or not

Add interceptors

Maybe cool to add interceptors for check request/response after actions?

Espresso Test

How to write a looping statement in Record Espresso Test using kotlin?

Idling resources to ignore the endless async task for espresso

Hi there,

It's not an issue, it's a question if you already have the way in the framework how to ignore the endless async tasks.
I've not found a community where to ask it.

Is there a way how to say espresso to not wait for the specific endless async task from the AsyncTask.THREAD_POOL_EXECUTOR ?

Thanks for the advice.

Custom assertion on action result

Sometimes operation executed successfully. But the action wasn't performed. It's usually a minor bug in application.
It's planned to add custom lambda assertion to check the operation really was executed. In case of failure, operation will be repeated.

Parameterized tests not working with custom name

Code sample:

open class BaseTest {

    val activityTestRule = createUltronComposeRule<MainActivity>()

    val beforeEachTestRule = SetUpRule("Precondition before each test").add {
              ...
    }
    
         @get:Rule
    open val ruleSequence = RuleSequence(activityTestRule, beforeEachTestRule)

    companion object {

        @BeforeClass
        @JvmStatic
        fun configuration() {
            UltronConfig.applyRecommended()
            UltronAllureConfig.applyRecommended()
            UltronComposeConfig.applyRecommended()
            UltronComposeConfig.addListener(ScreenshotAttachListener())
            UltronComposeConfig.addListener(DetailedOperationAllureListener())
        }
    }
}

@RunWith(Parameterized::class)
SubclassTest(private val testValue: String) : BaseTest() {
   
companion object {

 @JvmStatic
 @Parameters(name = "{0}")
 fun testData() = listOf(
  "testValue1",
  "testValue2",
 )
 }

@Test fun myAwesomeTest() {
   ...
   }
}
Screenshot 2023-06-28 at 19 03 31

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.