All Projects → KakaoCup → Compose

KakaoCup / Compose

Licence: Apache-2.0 License
Nice and simple DSL for Espresso Compose UI testing in Kotlin

Programming Languages

kotlin
9241 projects
ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to Compose

Kakao
Nice and simple DSL for Espresso in Kotlin
Stars: ✭ 1,109 (+1880.36%)
Mutual labels:  espresso, ui-testing, testing-framework
Device Automator
An easy to use, Espresso like, syntax on top of the Android UI Automator testing framework
Stars: ✭ 63 (+12.5%)
Mutual labels:  espresso, ui-testing
espresso-robot-pattern-sample
Espresso Robot Pattern Sample with Spoon Integration
Stars: ✭ 37 (-33.93%)
Mutual labels:  espresso, ui-testing
FactoryOrchestrator
A cross-platform system service which provides a simple way to run and manage factory line validation, developer inner-loop, diagnostics, and fault analysis workflows.
Stars: ✭ 36 (-35.71%)
Mutual labels:  testing-framework, testing-library
Katasuperheroeskotlin
Super Heroes Kata for Android Developers in Kotlin. The main goal is to practice UI Testing.
Stars: ✭ 77 (+37.5%)
Mutual labels:  espresso, ui-testing
api-test
🌿 A simple bash script to test JSON API from terminal in a structured and organized way.
Stars: ✭ 53 (-5.36%)
Mutual labels:  testing-framework, testing-library
Green Coffee
Android library that allows you to run your acceptance tests written in Gherkin in your Android instrumentation tests.
Stars: ✭ 219 (+291.07%)
Mutual labels:  espresso, ui-testing
Kotlin-MVP-Testing
Examples of testing different layers of an Android app with MVP architecture.
Stars: ✭ 24 (-57.14%)
Mutual labels:  espresso, android-testing
test-real-styles
(test-)framework agnostic utilities to test real styling of (virtual) dom elements
Stars: ✭ 37 (-33.93%)
Mutual labels:  ui-testing, testing-library
ui-testing
No description or website provided.
Stars: ✭ 15 (-73.21%)
Mutual labels:  espresso, android-testing
SpecTools
Write less test code with this set of spec tools. Swift, iOS, testing framework independent (but works well with Quick/Nimble or directly).
Stars: ✭ 38 (-32.14%)
Mutual labels:  ui-testing, testing-framework
kheera-testrunner-android
BDD Framework for Android
Stars: ✭ 18 (-67.86%)
Mutual labels:  testing-framework, android-testing
FragmentTestRule
JUnit Rule to test a Fragment in isolation
Stars: ✭ 102 (+82.14%)
Mutual labels:  espresso, android-testing
playwright-fluent
Fluent API around playwright
Stars: ✭ 71 (+26.79%)
Mutual labels:  testing-library
testkube
☸️ Kubernetes-native framework for test definition and execution
Stars: ✭ 172 (+207.14%)
Mutual labels:  testing-framework
page-content-tester
Paco is a Java based framework for non-blocking and highly parallelized Dom testing.
Stars: ✭ 13 (-76.79%)
Mutual labels:  testing-framework
masquerade
CUBA Platform UI Testing Library
Stars: ✭ 29 (-48.21%)
Mutual labels:  ui-testing
samples
Jetpack Compose based project, used to stress-testing compose features / integrations and explore non-trivial functionality
Stars: ✭ 21 (-62.5%)
Mutual labels:  compose
PlayWeather
🔥🔥🔥 Compose、Lce、MVVM、深色模式、横屏、无网弱网适配、Room、Hilt、多语言切换,目前Android最新的库基本全用上了,你想要的都有🔥🔥🔥
Stars: ✭ 120 (+114.29%)
Mutual labels:  compose
AnotherFasterRunner
基于HttpRunner+Python+DRF+Vue的接口自动化测试平台
Stars: ✭ 216 (+285.71%)
Mutual labels:  testing-framework

Kakao Compose

Kotlin version badge Telegram Telegram

Nice and simple DSL for Espresso Compose in Kotlin

coco

Benefits

  • Readability
  • Reusability
  • Extensible DSL
  • Interceptors

Concept

The one of the main concepts of Jetpack Compose is a presentation of UI according to UI tree (UI hierarchy) approach with a parent-children relationships support. It means that the related UI test library has to support a parent-children relationships for Nodes by default. It will be discovered below how Kakao Compose library supports mentioned parent-children relationships approach.

How to use it

Create Screen

Create your entity ComposeScreen where you will add the views involved in the interactions of the tests:

class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
    ComposeScreen<MainActivityScreen>(
        semanticsProvider = semanticsProvider
)

ComposeScreen can represent the whole user interface or a portion of UI. If you are using Page Object pattern you can put the interactions of Kakao inside the Page Objects.

Described way of Screen definition is very similar with the way that Kakao library offers. But, usually, Screen in Jetpack Compose is a UI element (Node) too. That's why there is an additional option to declare ComposeScreen:

class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
    ComposeScreen<MainActivityScreen>(
        semanticsProvider = semanticsProvider,
        viewBuilderAction = { hasTestTag("MainScreen") }
)

So, ComposeScreen is a BaseNode's inheritor in Kakao-Compose library. And, as you've seen above, there is a possibility to describe ComposeScreen without mandatory viewBuilderAction in cases when Screen is an abstraction without clear connection with any Node.

Create KNode

ComposeScreen contains KNode, these are the Jetpack Compose nodes where you want to do the interactions:

class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
    ComposeScreen<MainActivityScreen>(
        semanticsProvider = semanticsProvider,
        viewBuilderAction = { hasTestTag("MainScreen") }
) {

    val myButton: KNode = child {
        hasTestTag("myTestButton")
    }
}

myButton was declared as a child of MainActivityScreen. It means that myButton will be calculated using matchers specified in a lambda explicitly and a parent matcher implicitly (MainActivityScreen). Under the hood, the SemanticMatcher of myButton is equal to hasTestTag("myTestButton") + hasParent(MainActivityScreen.matcher).

Also, KNode can be declared as a child of another KNode:

class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
    ComposeScreen<MainActivityScreen>(
        semanticsProvider = semanticsProvider,
        viewBuilderAction = { hasTestTag("MainScreen") }
) {

    val myButton: KNode = child {
        hasTestTag("myTestButton")
    }

    val myButton2: KNode = myButton.child {
        hasTestTag("myTestButton2")
    }
}

myButton2 will be calculated with the following SemanticMatcher = hasTestTag("myTestButton") + hasParent(myButton.matcher) + hasParent(MainActivityScreen.matcher). But, we advise not to abuse inheritance and use only the following chain: "ComposeScreen" - "Element of ComposeScreen".

The last, KNode can be declared without child function using only explicit matchers:

class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
    ComposeScreen<MainActivityScreen>(
        semanticsProvider = semanticsProvider,
        viewBuilderAction = { hasTestTag("MainScreen") }
) {

    val myButton = KNode(this) {
        hasTestTag("myTestButton")
    }
}

Every KNode contains many matches. Some examples of matchers provided by Kakao Compose:

  • hasText
  • hasTestTag
  • and more

Like in Espresso you can combine different matchers:

val myButton = KNode(this) {
      hasTestTag("myTestButton")
      hasText("Button 1")
 }

Write the interaction.

The syntax of the test with Kakao is very easy, once you have the ComposeScreen and the KNode defined, you only have to apply the actions or assertions like in Espresso:

class ExampleInstrumentedTest {
    @Rule
    @JvmField
    val composeTestRule = createAndroidComposeRule<MainActivity>()

    @Test
    fun simpleTest() {
        onComposeScreen<MainActivityScreen>(composeTestRule) {
            myButton {
                assertIsDisplayed()
                assertTextContains("Button 1")
            }

            onNode {
                hasTestTag("doesNotExist")
            }.invoke {
                assertDoesNotExist()
            }
        }
    }
}

Intercepting

If you need to add custom logic during the Kakao-Compose -> Espresso(Compose) call chain (for example, logging) or if you need to completely change the events/commands that are being sent to Espresso during runtime in some cases, you can use the intercepting mechanism.

Interceptors are lambdas that you pass to a configuration DSL that will be invoked before calls happening from inside Kakao-Compose.

You have the ability to provide interceptors at two main different levels: Kakao-Compose runtime and any individual BaseNode instance. Interceptors in Kakao-Compose support a parent-children concept too. It means that any BaseNode inherits interceptors of his parents.

On each invocation of Espresso function that can be intercepted, Kakao-Compose will aggregate all available interceptors for this particular call and invoke them in descending order: Active BaseNode interceptor -> Interceptor of the parent Active BaseNode -> ... -> Kakao-Compose interceptor.

Each of the interceptors in the chain can break the chain call by setting isOverride to true during configuration. In that case Kakao-Compose will not only stop invoking remaining interceptors in the chain, but will not perform the Espresso call. It means that in such case, the responsibility to actually invoke Espresso lies on the shoulders of the developer.

Here's the examples of intercepting configurations:

class SomeTest {
    @Before
    fun setup() {
        KakaoCompose { // KakaoCompose runtime
            intercept {
                onComposeInteraction {
                    onAll { list.add("ALL") }
                    onCheck { _, _ -> list.add("CHECK") }
                    onPerform { _, _ -> list.add("PERFORM") }
                }
            }
        }
    }

    @Test
    fun test() {
        onComposeScreen<MyScreen> {
            intercept {
                onCheck { interaction, assertion -> // Intercept check() call
                    Log.d("KAKAO", "$interaction is checking $assertion")
                }
            }

            myView {
                intercept { // Intercepting ComposeInteraction calls on this individual Node
                    onPerform(true) { interaction, action -> // Intercept perform() call and overriding the chain
                        // When performing actions on this view, Kakao level interceptor will not be called
                        // and we have to manually call Espresso now.
                        Log.d("KAKAO_NODE", "$interaction is performing $action")
                        interaction.perform(action)
                    }
                }
            }
        }
    }
}

For more detailed info please refer to the documentation.

Setup

Maven

<dependency>
  <groupId>io.github.kakaocup</groupId>
  <artifactId>compose</artifactId>
  <version><latest version></version>
  <type>pom</type>
</dependency>

or Gradle:

dependencies {
    androidTestImplementation 'io.github.kakaocup:compose:<latest version>'
}

Contribution Policy

Kakao Compose is an open source project, and depends on its users to improve it. We are more than happy to find you interested in taking the project forward.

Kindly refer to the Contribution Guidelines for detailed information.

Code of Conduct

Please refer to Code of Conduct document.

License

Kakao Compose is open source and available under the Apache License, Version 2.0.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].