Kaspresso Documentation

repository·master·Indexed 23 days ago

https://github.com/kasperskylab/kaspresso

A comprehensive Android UI testing framework built on top of Espresso and UI Automator. Kaspresso provides a declarative Kotlin DSL (via Kakao and Kautomator), interceptors and flakySafely blocks to reduce test flakiness, and built-in support for Jetpack Compose and Allure. It includes a Device class and AdbServer for system-level interactions, such as executing ADB commands, managing permissions, and collecting logcat output.

Tokens
78.9K
Snippets
192
Records
316
Agent score
83%

What's inside Kaspresso

  1. What is Kautomator?

    master

    Kautomator is a Kotlin DSL wrapper for UI Automator. It is inspired by Kakao and designed to make UI Automator tests more readable, reusable, and extensible.

    Key benefits include:

    • Readability: Replaces verbose UI Automator code with a clean DSL.
    • Reusability: Allows defining UI elements in Page Objects.
    • Extensibility: Provides an easy way to create custom UI views.
    • Speed: Offers a mechanism to boost UI Automator performance by bypassing its default idle-wait timeouts via Kaspresso interceptors.
    // Traditional UI Automator (Hard to read)
    val uiObject = uiDevice.wait(Until.findObject(By.res("com.package", "editText")), 2_000)
    uiObject.text = "Kaspresso"
    
    // Kautomator DSL (Readable)
    mainScreen {
        simpleEditText {
            replaceText("Kaspresso")
            hasText("Kaspresso")
        }
    }
  2. Overview of Kaspresso capabilities

    master

    Kaspresso is an Android UI testing framework built on top of Espresso and UI Automator. It provides a declarative DSL and several advanced features to improve test stability and developer productivity:

    • Flakiness Protection: Built-in mechanisms to prevent flaky tests.
    • Jetpack Compose Support: Native support for testing Compose UIs.
    • Screenshot Testing: Native approach with dark theme support.
    • Enhanced UI Automator: Significantly faster execution of UI Automator commands (up to 10x faster).
    • Interceptors: A mechanism to intercept all actions and assertions in one place.
    • Page Object Pattern: Implemented out-of-the-box.
    • Rich Reporting: Detailed logs, view hierarchies, screenshots, and video recordings.
    • System Integration: Ability to call ADB commands and interact with Android system elements and other applications.
    • Ecosystem Support: Support for Allure and Robolectric, and easy migration from Espresso.
  3. Kaspresso ecosystem support

    master

    Kaspresso supports several key technologies for modern Android testing:

    • Robolectric: Run UI tests in a JVM environment with most interceptors (stability/readability) still working.
    • Allure: Generates detailed Allure reports for every test.
    • Jetpack Compose: Early access support for writing tests for Compose screens using the same DSL and principles as View-based tests.
    • Screenshot Testing: Automated screenshotting for localization verification.
  4. What is the Kaspresso `Device` abstraction?

    master

    The Device abstraction is a provider of specialized managers designed for performing "off-screen" work and interacting with the Android OS outside of your application's process. It allows you to control system-level settings, manage files, emulate hardware events, and interact with the Android system via ADB commands.

    Key characteristics:

    • Scope: The device instance is available via the device property within BaseTestContext scope and BaseTestCase.
    • Requirements: Most features require an AdbServer to be running and the android.permission.INTERNET permission to execute ADB commands.
    • Emulator Limitations: Certain features like phone call emulation or SMS receiving only work on emulators and are marked with the @RequiresAdbServer annotation.
    @Test
    fun test() =
        run {
            step("Open Simple Screen") {
                activityTestRule.launchActivity(null)
                device.screenshots.take("Additional_screenshot")
    
                MainScreen {
                    simpleButton {
                        isVisible()
                        click()
                    }
                }
            }
        }
  5. Compare GrantPermissionRule and device.permissions for permission testing

    master

    Kaspresso provides two ways to handle permissions, each suited for different testing goals:

    GrantPermissionRule

    • Best for: Simple tests where you just need the permission to be present to proceed.
    • Pros: Works seamlessly across all API levels without extra logic.
    • Cons: Cannot test the permission dialog itself; cannot test application behavior when a permission is denied; tests may fail if permissions were previously denied (requiring app reinstallation or adb intervention).

    device.permissions

    • Best for: Comprehensive UI testing of permission flows.
    • Pros:
      • Allows verifying if the permission request dialog is actually displayed (isDialogVisible()).
      • Enables testing both the acceptance (allowViaDialog()) and denial (denyViaDialog()) of permissions.
      • More resilient to permission changes during test execution.
    • Cons: Requires conditional logic or @SdkSuppress to handle API levels below 23.
  6. How KScreen and KBaseView work together

    master

    Kaspresso provides specialized view classes that inherit from KBaseView. Each class is designed for a specific type of UI element to ensure that only relevant assertions and actions are available.

    For example:

    • Use KButton for buttons (provides button-specific interactions).
    • Use KTextView for text elements (provides text assertions).
    • Use KEditText for input fields.

    This hierarchy ensures that you don't attempt to perform invalid actions, such as checking the text content of a ProgressBar.

  7. Understand Kaspresso Interceptors

    master

    Kaspresso uses two main types of interceptors that work under the hood for every Kakao and Kautomator action/assertion:

    1. Behavior Interceptors: Intercept calls to ViewInteraction, DataInteraction, WebInteraction, UiObjectInteraction, and UiDeviceInteraction to execute logic (e.g., retries, scrolling).
    2. Watcher Interceptors: Intercept calls to ViewAction, ViewAssertion, Atom, WebAssertion, UiObjectAssertion, UiObjectAction, UiDeviceAssertion, and UiDeviceAction to perform actions before the actual call.

    Warning: If you manually call Kakao.intercept or Kautomator.intercept in your test, Kaspresso's interceptors will no longer work for that specific screen or view.

  8. Use before and after blocks to manage device state

    master

    Kaspresso provides before and after blocks to ensure test stability by managing the device's initial and final states.

    • before block: Executes code before the main test logic. Use this to set default device settings, such as enabling Wi-Fi or setting a specific screen orientation.
    • after block: Executes code after the test completes. Use this to revert the device to its original state (e.g., restoring portrait orientation or re-enabling network connectivity), ensuring that side effects from one test do not impact subsequent tests.

    These blocks are chained before the .run { ... } block of a TestCase.

    class MyTest : TestCase() {
        @Test
        fun test() {
            before {
                // Setup code
                device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
                device.network.toggleWiFi(true)
            }.after {
                // Teardown code
                device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
                device.network.toggleWiFi(true)
            }.run {
                // Test logic using steps
            }
        }
    }
  9. How Kautomator interceptors work

    master

    Interceptors allow you to inject logic into the call chain Kautomator -> UI Automator. You can use them for logging or to completely override UiAssertion or UiAction calls.

    Interceptor Levels

    Interceptors can be provided at three levels:

    1. Kautomator runtime: Global interceptors.
    2. UiScreen level: Interceptors active for all elements within a specific screen.
    3. UiView level: Interceptors for a specific UI element instance.

    Execution Order

    When a function is called, Kautomator aggregates interceptors and executes them in this order: UiView interceptor $\rightarrow$ Active Screens interceptors $\rightarrow$ Kautomator interceptor.

    Overriding Calls

    An interceptor can stop the chain and prevent the actual UI Automator call by setting isOverride = true. If you override, you are responsible for manually executing the UI Automator call if needed.

    // Example: Overriding at the View level
    myView {
        intercept {
            onPerform(true) { uiInteraction, uiAction -> 
                // Kautomator runtime interceptors will NOT be called
                // You must manually call the interaction
                uiInteraction.perform(uiAction)
            }
        }
    }
    class SomeTest {
        @Before
        fun setup() {
            KautomatorConfigurator {
                intercept {
                    onUiInteraction {
                        onPerform { uiInteraction, uiAction -> 
                            testLogger.i("KautomatorIntercept", "interaction=$uiInteraction, action=$uiAction")
                        }
                    }
                }
            }
        }
    
        @Test
        fun test() {
            MyScreen {
                intercept {
                    onUiInteraction {
                        onCheck { uiInteraction, uiAssert -> 
                            testLogger.i("KautomatorIntercept", "interaction=$uiInteraction, assert=$uiAssert")
                        }
                    }
                }
    
                myView {
                    intercept {
                        onPerform(true) { uiInteraction, uiAction -> 
                            Log.d("KAUTOMATOR_VIEW", "$uiInteraction performs $uiAction")
                            uiInteraction.perform(uiAction) 
                        }
                    }
                }
            }
        }
    }
  10. Distinguish between `device.targetContext` and `device.context`

    master

    When running automated tests, two applications are typically present on the device: the application under test (the target) and the test runner application. Kaspresso provides two ways to access the Context via the device object:

    • device.targetContext: Provides access to the application under test (the target app). Use this when you want to interact with the resources or launch activities of your own app or a specific target.
    • device.context: Provides access to the test runner application (the app executing the test scenarios).
  11. Compare GrantPermissionRule and device.permissions

    master

    Kaspresso provides two ways to handle permissions. While GrantPermissionRule is a standard JUnit rule, using the device.permissions object is generally preferred for robust UI testing:

    • device.permissions advantages:
      • Allows verifying that the permission dialog is actually visible (isDialogVisible()).
      • Enables testing application behavior for both accepted (allowViaDialog()) and denied (denyViaDialog()) permission scenarios.
      • More resilient to state: If permissions are revoked via adb shell during a test, device.permissions handles it correctly, whereas GrantPermissionRule may cause a crash.
    • GrantPermissionRule limitations:
      • Cannot verify the presence of the dialog.
      • If a permission was previously denied, the rule might fail unless the app is reinstalled or permissions are reset via adb.
  12. How to use Kaspresso screenshots vs UIAutomator screenshots

    master

    While you can technically use device.uiDevice.takeScreenshot (from the UIAutomator library), you should always use Kaspresso's device.screenshots.take instead for the following reasons:

    1. Organization: Kaspresso organizes screenshots into predictable folders based on the test and step names, making them easy to locate.
    2. Enhanced Features: Kaspresso provides built-in improvements such as image scaling, quality configuration, and the ability to take full-screen screenshots when content exceeds the visible screen area.