Coder Social home page Coder Social logo

kasperskylab / kaspresso Goto Github PK

View Code? Open in Web Editor NEW
1.7K 27.0 145.0 119.37 MB

Android UI test framework

Home Page: https://kasperskylab.github.io/Kaspresso/

License: Apache License 2.0

Kotlin 98.24% Java 1.46% Makefile 0.05% Shell 0.25%
espresso kakao interceptors flakiness uiautomator android uiautomator2 dsl best-practices architecture kaspresso hacktoberfest

kaspresso's Introduction

Android Arsenal Android Weekly Android Weekly MavenCentral Build and Deploy Telegram Telegram Discord

Kaspresso

Kaspresso is a framework for Android UI testing. Based on Espresso and UI Automator, Kaspresso provides a wide range of additional features, such as:

  • Built-in protection against flaky tests
  • Jetpack Compose support
  • Screenshot testing with native approach (with dark mode support)
  • Declarative approach for writing tests
  • Ability to interact with other applications and system elements and interfaces
  • Human readability with Kotlin DSL wrappers over UiAutomator and Espresso
  • Detailed logs and reports (logs, view hierarchy, screenshots, video etc.)
  • ADB support
  • Allure support
  • Robolectric support
  • Easy migration from Espresso
  • Flexible configuration options
  • Automatic artifacts pulling after tests execution
  • Significantly faster execution of UI Automator commands. With Kaspresso, some UI Automator commands run 10 times faster!
  • Page object pattern from the box

And many more!

Kaspresso

Integration

To integrate Kaspresso into your project:

  1. If the mavenCentral repository does not exist, include it to your root build.gradle file:
allprojects {
    repositories {
        mavenCentral()
    }
}
  1. Add a dependency to build.gradle:
dependencies {
    androidTestImplementation 'com.kaspersky.android-components:kaspresso:<latest_version>'
    // Allure support
    androidTestImplementation "com.kaspersky.android-components:kaspresso-allure-support:<latest_version>"
    // Jetpack Compose support
    androidTestImplementation "com.kaspersky.android-components:kaspresso-compose-support:<latest_version>"
}

To try out the cutting edge kaspresso updates before an official release add a snapshot repository to your build.gradle

dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") }
    }
}

To use a snapshot version of a Kaspresso add a "-SNAPHOT" postfix to the latest Kaspresso version e.g.

dependencies {
    androidTestImplementation 'com.kaspersky.android-components:kaspresso:<latest_version>-SNAPSHOT'
}

If you are still using the old Android Support libraries, we strongly recommend to migrate to AndroidX.

The last version with Android Support libraries is:

dependencies {
    androidTestImplementation 'com.kaspersky.android-components:kaspresso:1.5.3'
}

FAQ

See our website. You can also reach out to us on Discord.

Tutorial NEW

To make it easier to learn the framework, a step-by-step tutorial is available on our website.

Capabilities of Kaspresso

Readability

We like the syntax that Kakao applies to write UI tests. This wrapper over Espresso uses the Kotlin DSL approach, that makes the code significantly shorter and more readable. See the difference:

Espresso:

@Test
fun testFirstFeature() {
    onView(withId(R.id.toFirstFeature))
        .check(ViewAssertions.matches(
               ViewMatchers.withEffectiveVisibility(
                       ViewMatchers.Visibility.VISIBLE)))
    onView(withId(R.id.toFirstFeature)).perform(click())
}

Kakao:

@Test
fun testFirstFeature() {
    mainScreen {
        toFirstFeatureButton {
            isVisible()
            click()
        }
    }
}

We used the same approach to develop our own wrapper over UI Automator, and we called it Kautomator. Take a look at the code below:

UI Automator:

val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
val uiDevice = UiDevice.getInstance(instrumentation)
val uiObject = uiDevice.wait(
    Until.findObject(
        By.res(
            "com.kaspersky.kaspresso.sample_kautomator",
            "editText"
        )
    ),
    2_000
)
uiObject.text = "Kaspresso"
assertEquals(uiObject.text, "Kaspresso")

Kautomator:

MainScreen {
    simpleEditText {
        replaceText("Kaspresso")
        hasText("Kaspresso")
    }
}

Since Kakao and Kautomator provide almost identical APIs, you don’t have to care about what is under the hood of your tests, either Espresso or UI Automator. With Kaspresso, you write tests in the same style for both.

However, Kakao and Kautomator themselves don't help you to see the relation between the test and the corresponding test case. Also, a long test often becomes a giant piece of code that is impossible to split into smaller parts. That's why we have created an additional Kotlin DSL that allows you to read your test more easily.

See the example below:

@Test
fun shouldPassOnNoInternetScanTest() =
    beforeTest {
        activityTestRule.launchActivity(null)
        // some things with the state
    }.afterTest {
        // some things with the state
    }.run {
        step("Open Simple Screen") {
            MainScreen {
                nextButton {
                    isVisible()
                    click()
                }
            }
        }
        step("Click button_1 and check button_2") {
            SimpleScreen {
                button1 {
                    click()
                }
                button2 {
                    isVisible()
                }
            }
        }
        step("Click button_2 and check edit") {
            SimpleScreen {
                button2 {
                    click()
                }
                edit {
                    flakySafely(timeoutMs = 7000) { isVisible() }
                    hasText(R.string.text_edit_text)
                }
            }
        }
        step("Check all possibilities of edit") {
            scenario(
                CheckEditScenario()
            )
        }
    }

Stability

Sometimes your UI test passes ten times, then breaks on the eleventh attempt for some mysterious reason. It’s called flakiness.

The most popular reason for flakiness is the instability of the UI tests libraries, such as Espresso and UI Automator. To eliminate this instability, Kaspresso uses DSL wrappers and interceptors.

UI test libraries acceleration

Let’s watch some short video that shows the difference between the original UI Automator (on the right) and the accelerated one (on the left).

Here is a short explanation of why it is possible.

Interceptors

We developed Kaspresso behavior interceptors on the base of Kakao/Kautomator Interceptors to catch failures.

Thanks to interceptors, you can do a lot of useful things, such as:

  • add custom actions to each framework operation like writing a log or taking a screenshot;
  • overcome flaky operations by re-running failed actions, scrolling the parent layout or closing the android system dialog;

and many more (see the manual).

Writing readable logs

Kaspresso writes its own logs, detailed and readable:

Ability to call ADB commands

Espresso and UI Automator don't allow to call ADB commands from inside a test. To fix this problem, we developed AdbServer (see the wiki).

Ability to work with Android System

You can use Kaspresso classes to work with Android System.

For example, with the Device class you can:

  • push/pull files,
  • enable/disable network,
  • give permissions like a user does,
  • emulate phone calls,
  • take screenshots,
  • enable/disable GPS,
  • set geolocation,
  • enable/disable accessibility,
  • change the app language,
  • collect and parse the logcat output.

(see more about the Device class).

Features screenshotting

If you develop an application that is available across the world, you have to localize it into different languages. When UI is localized, it’s important for the translator to see the context of a word or a phrase, that is the specific screen.

With Kaspresso, translators can automatically take a screenshot of any screen. It’s incredibly fast, even for legacy screens, and you don't have to refactor or mock anything (see the manual).

Configurability

You can tune any part of Kaspresso (read more).

Robolectric support

You can run your UI-tests on the JVM environment. Additionally, almost all interceptors improving stability, readability and other will work. Read more.

Allure support

Kaspresso can generate very detailed Allure-reports for each test: More information is available here.

Jetpack Compose support

Now, you can write your Kaspresso tests for Jetpack Compose screens! DSL and all principles are the same. So, you will not see any difference between tests for View screens and for Compose screens. More information is available here.

Samples

All samples are available in the samples folder.

Most of the samples require AdbServer. To start AdbServer you should do the following steps:

  1. Go to the Kaspresso folder
cd ~/Workspace/Kaspresso
  1. Start adbserver-desktop.jar
java -jar artifacts/adbserver-desktop.jar

Existing issues

All existing issues in Kaspresso can be found here.

Breaking changes

Breaking changes can be found here

Contribution

Kaspresso is an open source project, so you are welcome to contribute (see the Contribution Guidelines).

License

Kaspresso is available under the Apache License, Version 2.0.

Runner

If you looking for a Runner to execute your UI tests we strongly recommend to use Marathon. Marathon is a fast, platform-independent test runner focused on performance and stability. It offers easy to use platform implementations for Android and iOS as well as an API for use with custom hardware farms and more techstacks. Marathon

kaspresso's People

Contributors

aartikov avatar andrethlckr avatar antipovandrey avatar areyana avatar authoress avatar avesha avatar azamatcherchesov avatar dsvoronin avatar eakurnikov avatar el-qq avatar ele638 avatar fullik avatar klbot avatar matzuk avatar mfglushchenko avatar nikitae57 avatar phansier avatar pkozlovskiy avatar priomino avatar pstrelchenko avatar recursia avatar ruslanmingaliev avatar sanmiade avatar sergio-sastre avatar sumin93 avatar v1sar avatar vacxe avatar valeryvpetrov-dev avatar vladislavsumin avatar xanderblinov 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kaspresso's Issues

Wiki. How to write autotests. Improvements

Possible additional topics:

  1. Who is responsible for pushing the entire autotests process.
  2. The strategy of how to execute autotests. E2E, functional tests. What about tests in PR.
    Don't forget this class - ClassificatorAnnotations.kt.

Add callback to flakySafely for tracking actual timeout

There is an idea to add callback to flakySafely function of actual timeout needed to pass the action.
Now timeouts in tests are set according to internal feeling of developer.
After such callback will be add there will be an opportunity to have a statistics on it and to configure actual timeout according to this statistics.
Therefore in case of fail tests will not wait for extra timeout and will fail earlier.

Add the possibility to make FullView screenshots.

Add the possibility to make FullView screenshots.
What are the FullView screenshots? When your feature's screen doesn't fit a device's screen then you have to make some screenshots scrolling your screen and, further, glue photos into a single screenshot.

Add capturing of elements on screen for External tests

Please add capturing of external screens for external screens.
On any test finished or on test failure.

It is possible with these commands:
adb shell uiautomator dump
adb pull /sdcard/window_dump.xml dump.xml

This will create dump.xml file with all elements on screen with their properties.

Кастомизация Kaspresso.Builder

Кейс:

Меня полностью устраивают настройки по умолчанию, но я хочу поменять лишь flakySafetyParams.timeoutMs

Как я сделал (первое, что пришло в голову):

kaspressoBuilder = Kaspresso.Builder.default().apply {
        flakySafetyParams = FlakySafetyParams().apply {
            timeoutMs = 5 * 1000L
        }
}

Но это не отработало, т.к. viewBehaviorInterceptors создаётся в default() с FlakySafetyParams(), и моё создание нового объекта никак не влияет на него.

Чтобы viewBehaviorInterceptors использовал мои настройки, нужно делать:

kaspressoBuilder = Kaspresso.Builder.default().apply {
        flakySafetyParams = flakySafetyParams.apply { 
            timeoutMs = 5 * 1000L
        }
}

То есть менять существующий, а не создавать новый flakySafetyParams.

Мои предложения:

  1. Создание viewBehaviorInterceptors перенести из default() в build()
    или
  2. Сделать flakySafetyParams val, а не var. Чтобы было нельзя создать новый объект, а только менять текущий

Мой кейс связан только с viewBehaviorInterceptors и flakySafetyParams, но это касается и других Interceptors и полей

Automation of upgrade-test support

There is an idea to automate upgrade-tests.

  1. Create a separate module consisting of just empty Application. This Application will be App under the test (not your original app!).
  2. At the test, you work with ui-automator and AdbServer. You install/start/end/remove old and new versions of your application by AdbServer. You interact with your application by ui-automator.

Additional idea: to introduce a beauty dsl to work with ui-automator like with Espresso by Kakao. Separate issue - #21.

Disable autofill service before the test starts

The simplest thing can be done to avoid the appearance of autofill dialog is to call context.getSystemService(AutofillManager::class.java).disableAutofillServices() before the test starts. This can be done via implementing the DefaultTestRunWatcherInterceptor which will make such a call before every test.

Make hasText() error more human readable.

In case if we use in our tests checks like:

. . .
control {
  hasText("bla bla")
}
. . . 

And this check will be failed. We'll get output like:


E/KASPRESSO: All attempts to interact for 2000 ms totally failed because of AssertionFailedError
E/KASPRESSO: junit.framework.AssertionFailedError: 'view has effective visibility=VISIBLE' doesn't match the selected view.
    Expected: bla bla
         Got: CustomFontTextView{id=2131362469, res-name=text_sign_in_button, visibility=VISIBLE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, layout-params=android.widget.LinearLayout$LayoutParams@cec54a4, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Sign in, input-type=0, ime-target=false, has-links=false}
        at dalvik.system.VMStack.getThreadStackTrace(Native Method)

Will be great to have standard hamcrest output like:

Expected: "bla bla"
Got: "ops it's devops =) "

Dependency conflict with com.intellij:annotations:12.0

Hello! I am trying to implement Kaspresso in my project, but i have conflicts with core annotations library:


* What went wrong:
Execution failed for task ':core:checkDebugAndroidTestDuplicateClasses'.
> 1 exception was raised by workers:
  java.lang.RuntimeException: Duplicate class org.intellij.lang.annotations.Identifier found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$AdjustableOrientation found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$BoxLayoutAxis found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$CalendarMonth found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$CursorType found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$FlowLayoutAlignment found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$FontStyle found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$HorizontalAlignment found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$InputEventMask found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$ListSelectionMode found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$PatternFlags found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$TabLayoutPolicy found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$TabPlacement found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$TitledBorderJustification found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$TitledBorderTitlePosition found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.JdkConstants$TreeSelectionMode found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.Language found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.MagicConstant found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.Pattern found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.PrintFormat found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.PrintFormatPattern found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.RegExp found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.intellij.lang.annotations.Subst found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.jetbrains.annotations.Nls found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.jetbrains.annotations.NonNls found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.jetbrains.annotations.NotNull found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.jetbrains.annotations.Nullable found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.jetbrains.annotations.PropertyKey found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)
  Duplicate class org.jetbrains.annotations.TestOnly found in modules adbserver-device-1.0.0.jar (com.kaspersky.android-components:adbserver-device:1.0.0) and annotations-12.0.jar (com.intellij:annotations:12.0)

I have tried to exclude org.jetbrains.annotations module from kaspresso dependency in implementing dependency process, but it couldn't help. Please, give me the way, how i can resolve that:)

My gradle code:

dependencies {
    androidTestImplementation libraries.junitAndroid
    androidTestImplementation( libraries.kaspresso ){
        exclude(group: 'com.intellij', module: 'annotations')
    }
    androidTestImplementation libraries.espressoCore
    androidTestImplementation libraries.uiAutomator
    androidTestImplementation libraries.kakao
    androidTestImplementation libraries.espressoWeb
    androidTestImplementation libraries.espressoContrib
    androidTestImplementation libraries.espressoIntents 
}

dependencies.gradle:

        junitAndroid            : "androidx.test.ext:junit:1.1.1",
        kaspresso               : "com.kaspersky.android-components:kaspresso:1.0.1",
        espressoCore            : "androidx.test.espresso:espresso-core:3.2.0",
        uiAutomator             : "androidx.test.uiautomator:uiautomator:2.2.0",
        kakao                   : "com.agoda.kakao:kakao:2.2.0,
        espressoWeb             : "androidx.test.espresso:espresso-web:3.2.0",
        espressoContrib         : "androidx.test.espresso:espresso-contrib:3.2.0",
        espressoIntents         : "androidx.test.espresso:espresso-intents:3.2.0",

Make before and after sections optional

Very often it is not necessary to specify actions to perform before and after the test, so users would like not to create empty before and after sections before the run block but to have an access straight to run section when they start writing a test.

Provide a better way to tweak the default Kasspresso.Builder

It is hard to change something in the default Kaspresso.Builder without breaking of consistency. For example, to change a screenshots directory this code is required:

 fun Kaspresso.Builder.changeScreenshotsDir(screenshotDir: File) {
    screenshots = ScreenshotsImpl(libLogger, activities, screenshotDir)

    stepWatcherInterceptors.replaceAll {
        if (it is ScreenshotStepWatcherInterceptor) {
            ScreenshotStepWatcherInterceptor(screenshots)
        } else {
            it
        }
    }

    testRunWatcherInterceptors.replaceAll {
        if (it is TestRunnerScreenshotWatcherInterceptor) {
            TestRunnerScreenshotWatcherInterceptor(screenshots)
        } else {
            it
        }
    }
}

Without calls of replaceAll interceptors will use old screenshots implementation.

Can this api be more convenient?

Circle CI improvements

Include a stage of source code compiling.
The reason. Sometimes, I've observed green build when the project didn't build.

Problems with screenshotting before/after a test

Stacktrace:
E/UiDevice: failed to save screen shot to file
java.io.FileNotFoundException: /storage/emulated/0/screenshots/com.kaspersky.kaspressample.simple_tests.SimpleTest/test/1578485958639_SimpleTest_step_1.png: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:496)

Additional information:
screenshot with the error

Dokka

Set dokka to work with at least two modules: kaspresso and kautomator

Provide few types of default `Kaspresso` implementations

Currently Kaspresso.default() is feature-loaded (e.g. contains a screenshot interceptor for each step).

As discussed, default implementation should be more lightweight and we can provide few more reconfigured implementations for different hypothetical environments (e.g. local, CI etc).

IsNotDisplayed returns NotFoundElement

I tryed to write something like this:

          AutologinScreen {
                compose {
                    or(quickSignIn) { isNotDisplayed() }
                    or(quickSignIn) { click() }
                }
            }

But it returns Element not found for isNotDisplayed()

Bug in Composer

After successfully executing a branch of a composed action, other branches is being executed too. Also, if the composed action was executed successfully, an error message is still displayed in the log:
Снимок экрана 2020-04-12 в 13 38 36

Написать полную документацию

Какие вещи подметил, и которые желательно отметить в доке:

  • Если вьюшка не видна (она отсутствует, visibility = gone, invisible), то uiObject2 = null
  • Действия никак не проверяются. Клик по задизейбленной кнопке будет валидным. Правда, такое же поведение и в Espresso. Но благодаря интерсепторам и обязательному UiActionType, мы можем отлавливать конкретное действие и добавлять какой-то ассершен. Например, доп.ассерт на ввод текста (проверять, если ли в поле текст реально).
  • Отметить, что для работы подкапотной скриншотилки, нужно добавлять в тест (может в базовом тест-кейсе сразу прописать?)
@get:Rule
    val runtimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_EXTERNAL_STORAGE
    )

Detailed exception in case of Screenshotting under the hood

When a developer writes a test, extends from BaseTestCase/TestCase with default Kaspresso settings and doesn't write inside the test permission granting rule then he can get an exception that permissions denied.
The possible solution is to catch such exceptions and output a detailed message where there will be an instruction to write permission granting rule.

Keyword:
java.io.FileNotFoundException: /storage/emulated/0/screenshots/run_1/com.kaspersky.kaspressample.simple_tests.SimpleTest/test/Additional_screenshot.png: open failed: EACCES (Permission denied)

The rule:

@get:Rule
    val runtimePermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.READ_EXTERNAL_STORAGE
    )

device.keyboard.typeText for russian characters

device.keyboard.typeText("йцуке")
throws
com.kaspersky.kaspresso.internal.exceptions.AdbServerException: command=input text й was performed with failed result=CommandResult(status=FAILED, description=exitCode=137, message=)

tested with:

  • kaspresso 1.0.1
  • emulator Pixel 2 API 25.

Реализовать удобный и понятный API для ожидания появления View или его состояния

В Telegram-чате Kaspresso&AdbServer support [RU] обсуждали идею удобного и интуитивно понятного API для ситуаций, когда необходимо ожидать появления View в иерархии (аналог Espresso Idling Resource).

Проблематика: сейчас это можно реализовать с помощью flakySafely + метода isVisible(), но это неудобно (нужно оборачивать в before{ }.after{ }.run{ }), неочевидно и решает не совсем ту задачу, о которой я пишу.

Хотелось бы иметь возможность явно указать, что View может изначально не быть в иерархии (ViewNotFoundException). Какие варианты мне видятся наиболее лаконичными (в порядке убывания):

  1. Использование аннотации (@Idling, @idle, @IdlingResource) при инициализации KView. Например:
class MainActivityScreen : Screen<MainActivityScreen>() {    
    @Idling(maxWaitMillis = 60_000)
    val moreBtn = KButton {withId(R.id.btn_more)}
}
  1. С помощью дополнительного метода в самом тесте, например, wait(maxWaitMillis = 60_000):
@Test
    fun mainActivityTest() {
        mainActivityScreen{
            moreBtn {
                wait(maxWaitMillis = 60_000)
                click()
            }
        }
    }

Второй вариант делает чтение теста более понятным (т.к. аннотация находится в другом классе, ее не будет видно из класса с тестами, в отличие от метода wait()). Можно конечно заморочиться и для аннотированных объектов генерировать объект с именем moreBtnIdle и в тесте использовать уже его, но это полет фантазии уже)

Также часто возникают ситуации, когда нужно дождаться состояния уже существующего в иерархии View (например, дождаться состояния кнопки isEnabled, а затем кликнуть по ней).

Пусть это будет метод waitFor{} (waitForState{}, waitState{}) в который можно поместить assert-ы которые доступны только для объявленного KView (в данном случае KButton). Также должна быть возможность указать кастомные matcher-ы/assert-ы

Применив идею на примере выше, было бы отлично писать код в следующем стиле:

@Test
    fun mainActivityTest() {
        mainActivityScreen{
            moreBtn {
               wait(maxWaitMillis = 60_000) //или аннотация @Idling
               waitFor(maxWaitMillis = 60_000) {
                      isEnabled()
               }
               click()
               waitFor(maxWaitMillis = 60_000) {
                      withText(R.string.new_btn_text)
               }
            }
        }
    }

В результате тест можно было трактовать так: ждать появления в иерархии moreBtn максимум 60 секунд. После того, как кнопка появится, ждать состояния isEnabled и после этого выполнить по ней клик, после чего дождаться текста R.string.new_btn_text

Был бы очень рад такому API. Возможно, вы разовьете идею и придумаете еще более элегантное решение. Заранее спасибо)

DeviceLocationSampleTest не проходит

я скачал sample app. Пытаюсь запустить DeviceLocationSampleTest но он не проходит. Можете пожалуйста посмотреть что для неё нужно?

Interceptors for before/after sections

Sometimes there is the need to execute some actions with BaseTestContext in every test in before/after sections. Annotations like @beforeEachTest or something else don't work because BaseTestContext is not available outside before-after-run sections.
Also, it will be more flexible when a developer has the ability to set/add/modify sets of start/end actions for all tests (or some tests) from a single place.

FlakySafely analogue for negative scenarios

Kaspresso has flakySafely function for positive scenarios like check the dialog appears.
It does during some period cycle: look if dialog is visible, if not - wait some period and repeat. If visible - test goes out of flakySafely and continues.
But this fun will not work for negative scenarios, like check if dialog doesn't appear .
It could look like this: during some period cycle: look if dialog is not visible, if not - wait some period and repeat. If visible - test fails.

If we will add our check of the dialog not visible in flakySafely - it will check only first time and the test will continue.

Remove timems from names of DocLoc screenshots

Names of DocLoc screenshots are the following:
1570158949597_1__Simple_screen.png
1570158950868_2__Simple_fragment_-two_buttons.png
1570158954644_3__Simple_fragment
-input.png
1570158956282_4__Simple_fragment
-_typed_text.png

It's not readable and it prevents to automate some processes for DocLoc.
But "timems" is useful for common screenshotting during the test because it allows saving all screenshots of all attempts to pass the test.

DocLocScreenshotTestCase does not save failure screenshots

Steps to reproduce:

  1. Change kaspressample.docloc_tests.ScreenshotSampleTest to make it failing.
  2. Run the test.
  3. Open "sdcard/screenshots/en" in Device File Explorer.

Expected behavior: "fails" directory contains a failure screenshot.

Actual behavior: "fails" directory is empty. Logcat has java.lang.IllegalArgumentException: Could not find test class!

Tested on Emulator API 26, x86_64.

Android App compilation issue when Sample app is written in java

Hi ,
I my sample android app in java and wrote Kaspresso sample test in koltin .
I am facing unknown compilation issue .
But same test app works fine when sample android app is written in koltin .

So My query is direct .
Do Kaspresso support Java ?

  1. Can we use kaspresso for Instrumentation testing for an android app written in Java ?
  2. Can we write kaspresso test in Java ?

flakySafetyParams details

  1. If you want to set flakySafetyParams in Kaspresso.Builder you have to use such code:
open class KisaTestCase : TestCase(
    kaspressoBuilder = Kaspresso.Builder.default().apply {
        flakySafetyParams.apply {
            timeoutMs = Time.Five.seconds
        }
    }
) {

It's a consequence of some incorrect order of variables in Kaspresso.Builder.
We are going to fix it.
2. I have concluded to increase default flakySafetyParams.timeoutMs param to 5 sec after some experiments.
Also, I'll change it.

Создать в вики раздел Best Practices

  • перенести туда с выступления на декабрьском воркшопе
  • перенести тему обсуждения про многомодульные проекты (в сообщениях в телеге)

Задача на @ele638
Ориентировочное время исполнения - 29.03.2020

Dsl over ui-automator

Dsl will look outside and inside like Kakao plus all similar Interceptors.
There will be base classes like UaBaseView, UaBaseAssertions, UaBaseActions.

The first draft looks:

step("click") {
    mainScreenFb(this) {
        simpleButton {
            click()
        }
   }
}

class MainScreenFb : ExtraScreen<MainScreenFb>() {

    override val layoutId: Int? = R.layout.activity_main
    override val viewClass: Class<*>? = MainActivity::class.java

    val simpleButton = UiObjectSafe(
        uiDevice,
        composeProvider,
        "com.kaspersky.kaspressample",
        R.id::activity_main_button_simple.name
    )

}

abstract class ExtraScreen<out T : ExtraScreen<T>> {

    abstract val layoutId: Int?
    abstract val viewClass: Class<*>?

    protected lateinit var uiDevice: UiDevice
    protected lateinit var composeProvider: ComposeProvider

    @Suppress("UNCHECKED_CAST")
    operator fun invoke(ref: BaseTestContext, function: T.() -> Unit) {
        uiDevice = ref.device.uiDevice
        composeProvider = ref
        function.invoke(this as T)
    }

}

class UiObjectSafe(
    private val uiDevice: UiDevice,
    private val composeProvider: ComposeProvider,
    private val packageName: String,
    private val resId: String
) {

    operator fun invoke(function: UiObject2.() -> Unit) {
        uiDevice.wait(
            Until.findObject(
                By.res(packageName, resId)
            ),
            2000 // put timeOutMs from FlakySafetyParams
        )
            .apply(function) // put compose method wrapping over function, but it's a topic to think out
                             // we need to warn developers that they must put in lambda only one action
                             // otherwise the retry may be incorrect (one action is correct, second is not => compose retries two actions)
    }

}

[Kautomator] Поддержка ресурсов Android

Сейчас невозможно использовать ресурсы R.id и R.string в Кавтоматоре. Неплохо было бы добавить поддержку этого, как в какао. Для тестов своего же приложения (апрейд-сценариев) можно копировать ресурсы с помощью Gradle-плагина (пример реализации есть в ButterKnife).

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.