<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kaspresso</title>
    <description>The latest articles on Forem by Kaspresso (@kaspresso).</description>
    <link>https://forem.com/kaspresso</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1279867%2F997ebbce-b052-4647-9371-d76dafdc3301.png</url>
      <title>Forem: Kaspresso</title>
      <link>https://forem.com/kaspresso</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kaspresso"/>
    <language>en</language>
    <item>
      <title>How to make Espresso tests more readable and stable</title>
      <dc:creator>Kaspresso</dc:creator>
      <pubDate>Wed, 03 Apr 2024 12:50:43 +0000</pubDate>
      <link>https://forem.com/kaspresso/how-to-make-espresso-tests-more-readable-and-stable-5ffo</link>
      <guid>https://forem.com/kaspresso/how-to-make-espresso-tests-more-readable-and-stable-5ffo</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1ff5tys4368ff54ahv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1ff5tys4368ff54ahv1.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
If you have ever written tests on Espresso, the open-source testing framework from Google, you know that they are not always stable and easy to read. My name is Ksenia Nikitina, and I am an Android developer at Kaspersky. In this article, I will provide you with a way to make your autotests meet all the key qualities, including high readability, stability, logability, possibility of taking screenshots, compatibility with AndroidOS, and, finally, a well-thought-out and intuitive architecture.&lt;/p&gt;

&lt;p&gt;So, when there is a need to write autotests on Android, we most often turn to the Espresso framework by Google (if you are not familiar with Espresso, you can find out more about it in &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fdeveloper.android.com%2Ftraining%2Ftesting%2Fespresso" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt;). Espresso allows you to work with application elements natively and using the &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FWhite-box_testing" rel="noopener noreferrer"&gt;white-box&lt;/a&gt; method. You can first find the necessary elements on the screen using matchers, and then perform various actions or tests on them.&lt;/p&gt;

&lt;p&gt;Let's look at how the autotests work using a specific example. You can launch the application and reproduce all the steps described below by yourself by downloading &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fkas.pr%2Fmedium-nikitina-kaspresso-espressotests-klgithub" rel="noopener noreferrer"&gt;the source code on our GitHub&lt;/a&gt; and launching the Tutorial project.&lt;/p&gt;

&lt;p&gt;When we open the application, we see a screen with the "Internet Availability" button, and when we click on it, we navigate to the Wifi status check screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ispfg80wue6lbhfxug7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ispfg80wue6lbhfxug7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Check Wifi status" button is clickable. Once clicked, the current Wifi status is displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4ikrw8nb4iyjrdgeama.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4ikrw8nb4iyjrdgeama.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also important to us that when we rotate the screen, the text indicating the Wifi status does not change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh3xmafldi5hytfxdrdw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh3xmafldi5hytfxdrdw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To sum it up, to verify that the application works correctly, we need to go through the following steps:&lt;/li&gt;
&lt;li&gt;Before starting the test, set the device to landscape mode, turn on Wifi, and launch the main screen of the application.&lt;/li&gt;
&lt;li&gt;Check that the Internet Availability button is visible and clickable.&lt;/li&gt;
&lt;li&gt;Check that the header indicating the Wifi status does not contain any text.&lt;/li&gt;
&lt;li&gt;Click the "Check Wifi status" button.&lt;/li&gt;
&lt;li&gt;Check that the header text changed to "enabled".&lt;/li&gt;
&lt;li&gt;Disable Wifi.&lt;/li&gt;
&lt;li&gt;Click the "Check Wifi status" button.&lt;/li&gt;
&lt;li&gt;Check that the header text changed to "disabled".&lt;/li&gt;
&lt;li&gt;Flip the device.&lt;/li&gt;
&lt;li&gt;Check that the header text is still "disabled".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's try to automate this text test case and use it as an example to discuss the existing shortcomings of Espresso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Espresso test example
&lt;/h2&gt;

&lt;p&gt;Our test class using the Espresso framework:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package com.kaspersky.kaspresso.tutorial 

import androidx.test.espresso.Espresso.onView 
import androidx.test.espresso.action.ViewActions 
import androidx.test.espresso.assertion.ViewAssertions.matches 
import androidx.test.espresso.matcher.ViewMatchers.isClickable 
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed 
import androidx.test.espresso.matcher.ViewMatchers.withId 
import androidx.test.espresso.matcher.ViewMatchers.withText 
import androidx.test.ext.junit.rules.activityScenarioRule 
import androidx.test.ext.junit.runners.AndroidJUnit4 
import androidx.test.platform.app.InstrumentationRegistry 
import org.junit.Rule 
import org.junit.Test 
import org.junit.runner.RunWith 

@RunWith(AndroidJUnit4::class) 
class WifiSampleEspressoTest { 

    @get:Rule 
    val activityRule = activityScenarioRule&amp;lt;MainActivity&amp;gt;() 

    @Test 
    fun test() { 
        // launch target screen 
        onView(withId(R.id.wifi_activity_btn)).check(matches(isDisplayed())) 
        onView(withId(R.id.wifi_activity_btn)).check(matches(isClickable())) 
        onView(withId(R.id.wifi_activity_btn)).perform(ViewActions.click()) 
        // set portrait orientation 
        val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation 
        uiAutomation.executeShellCommand(SHELL_COMMAND_AUTO_ROTATE_DISABLE) 
        uiAutomation.executeShellCommand(SHELL_COMMAND_PORTRAIT_ORIENTATION) 

        // test 
        onView(withId(R.id.wifi_status)).check(matches(withText(""))) 
        onView(withId(R.id.check_wifi_btn)).check(matches(isDisplayed())) 
        onView(withId(R.id.check_wifi_btn)).check(matches(isClickable())) 
        onView(withId(R.id.check_wifi_btn)).perform(ViewActions.click()) 
        onView(withId(R.id.wifi_status)).check(matches(withText(R.string.enabled_status))) 

        // Turning off wifi 
        uiAutomation.executeShellCommand(SHELL_COMMAND_TURN_OFF_WIFI) 

        // wait for switching wifi 
        Thread.sleep(3000) 

        // test 
        onView(withId(R.id.check_wifi_btn)).perform(ViewActions.click()) 
        onView(withId(R.id.wifi_status)).check(matches(withText(R.string.disabled_status))) 

        //rotate 
        uiAutomation.executeShellCommand(SHELL_COMMAND_AUTO_ROTATE_DISABLE) 
        uiAutomation.executeShellCommand(SHELL_COMMAND_LANDSCAPE_ORIENTATION) 

        // wait for rotation 
        Thread.sleep(3000) 

        // test 
        onView(withId(R.id.wifi_status)).check(matches(withText(R.string.disabled_status))) 
    } 

    private companion object { 

        const val SHELL_COMMAND_AUTO_ROTATE_DISABLE = "content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0" 
        const val SHELL_COMMAND_PORTRAIT_ORIENTATION = "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:0" 
        const val SHELL_COMMAND_LANDSCAPE_ORIENTATION = "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:1" 
        const val SHELL_COMMAND_TURN_OFF_WIFI = "svc wifi disable" 
    } 
} 


```Kotlin

In this example, we realized that it was impossible to implement the test using Espresso alone, so we had to bring in the UIAutomator library. The test runs and allows you to check if the application is working correctly, but, unfortunately, the code has several drawbacks at the moment.

What we would like to improve in the code: separate the logic of element descriptions and the sequence of actions and checks; use a declarative style of writing tests; eliminate multiple repetitive onView calls and hierarchical nesting. Unfortunately, it is impossible to cover all the needs for Android autotesting with Espresso alone due to the lack of certain features.

## Code improvement
Let's try rewriting the code of our test by resorting to the open-source framework [Kaspresso](https://kas.pr/medium-nikitina-kaspresso-espressotests-kaspresso), which takes a declarative approach to writing tests. Let's look at a specific section of the code.

It looked like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;onView(withId(R.id.wifi_activity_btn)).check(matches(isDisplayed())) &lt;br&gt;
onView(withId(R.id.wifi_activity_btn)).check(matches(isClickable())) &lt;br&gt;
onView(withId(R.id.wifi_activity_btn)).perform(ViewActions.click()) &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
And now it turns out like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;wifiActivityButton {&lt;br&gt;&lt;br&gt;
    isVisible()&lt;br&gt;&lt;br&gt;
    isClickable()&lt;br&gt;&lt;br&gt;
    click()&lt;br&gt;&lt;br&gt;
} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Kaspresso uses [Kakao](https://github.com/KakaoCup/Kakao), a Kotlin-DSL wrapper for Espresso. The Kaspresso open-source framework inherited two main concepts from Kakao. The first is KView - a special representation of the UI elements that the test interacts with. Using KView saves us from constantly calling the onView method; now we just need to add matchers in the KView constructor once.

The second concept is the Screen class (implementation of the [Page Object](https://kasperskylab.github.io/Kaspresso/en/Wiki/Page_object_in_Kaspresso/) pattern), which describes all the elements that the test interacts with. The concept comes from web development and is about creating a description of the screen visible to the user. This object does not contain any logic. This allows you to describe Screens and their elements in a separate file and interact with them from the test class code in a declarative style. Thus, Page Objects are completely independent, which makes them reusable to the max.

Let's rewrite our test class using the Kaspresso framework. If you run into problems with any of the following steps, you can find a ready-made version of the autotest in the [tutorial_results](https://github.com/KasperskyLab/Kaspresso/tree/TECH-tutorial-results) branch.

The first step is to create the MainScreen Page Object:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial.screen &lt;/p&gt;

&lt;p&gt;import com.kaspersky.kaspresso.screens.KScreen &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.R &lt;br&gt;
import io.github.kakaocup.kakao.text.KButton &lt;/p&gt;

&lt;p&gt;object MainScreen : KScreen() { &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;override val layoutId: Int? = null 
override val viewClass: Class&amp;lt;*&amp;gt;? = null 

val wifiActivityButton = KButton { withId(R.id.wifi_activity_btn) } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Here we need to determine the ID of the layout installed on the screen (layoutId) and the name of the class (viewClass). This is required to link the test to a specific layout file and the activity or fragment class. Linking them makes the tasks of further support and development of the test more convenient, but for now, we are faced with the task of considering a demo test class, so let's leave the value at "null".

Next, let's create a WifiScreen:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial.screen &lt;/p&gt;

&lt;p&gt;import com.kaspersky.kaspresso.screens.KScreen&lt;br&gt;&lt;br&gt;
import com.kaspersky.kaspresso.tutorial.R&lt;br&gt;&lt;br&gt;
import io.github.kakaocup.kakao.text.KButton&lt;br&gt;&lt;br&gt;
import io.github.kakaocup.kakao.text.KTextView  &lt;/p&gt;

&lt;p&gt;object WifiScreen : KScreen() {  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;override val layoutId: Int? = null  

override val viewClass: Class&amp;lt;*&amp;gt;? = null  

val checkWifiButton = KButton { withId(R.id.check_wifi_btn) }  
val wifiStatus = KTextView { withId(R.id.wifi_status) }  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Now let's get down to the test cases. The test class should be inherited from the TestCase class. Let's create such a WifiSampleTest class and add a method annotated with a @Test (import org.junit.Test) to check how the application works.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial &lt;/p&gt;

&lt;p&gt;import androidx.test.ext.junit.rules.activityScenarioRule &lt;br&gt;
import com.kaspersky.kaspresso.device.exploit.Exploit &lt;br&gt;
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.MainScreen &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen &lt;br&gt;
import org.junit.Rule &lt;br&gt;
import org.junit.Test &lt;/p&gt;

&lt;p&gt;class WifiSampleTest : TestCase() { &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@get:Rule 
val activityRule = activityScenarioRule&amp;lt;MainActivity&amp;gt;() 

@Test 
fun test() { 
    MainScreen { 
        wifiActivityButton { 
            isVisible() 
            isClickable() 
            click() 
        } 
    } 
    WifiScreen {         
        device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) 
        wifiStatus.hasEmptyText() 
        checkWifiButton { 
            isVisible() 
            isClickable() 
            click() 
        } 
        wifiStatus.hasText(R.string.enabled_status) 
        device.network.toggleWiFi(false) 
        checkWifiButton.click() 
        wifiStatus.hasText(R.string.disabled_status) 
        device.exploit.rotate() 
        wifiStatus.hasText(R.string.disabled_status) 
    } 
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
We mentioned earlier that Espresso doesn't work with Android OS, so users have to resort to the UIAutomator library. Note that the test code uses an instance of the Device class, which is part of the Kaspresso framework. This object has many useful methods suitable for interacting with Android OS. You can read more about them [here](https://kasperskylab.github.io/Kaspresso/Wiki/Working_with_Android_OS/).

What did we achieve?

1. Screen descriptions and testing the logic of their work are now separated. Test cases can now be described independently of Screens, and Screen classes can be reused in different tests.
2. The code has become much more readable, and it is now possible to write tests in a declarative style. We simply indicate, what actions and checks we want to perform and on which Screen.

## Code separation according to the steps of the test cases
The test class code is already looking better, but it still has some issues. Usually, any tests (including manual ones) are performed according to test cases. That is, the tester has a sequence of steps that they follow to check the Screen performance Now imagine that our test class contains way more steps and, accordingly, lines of code. Because it is unclear where one step ends and another one begins, it gets extremely difficult to understand and expand. We can mitigate this problem by using comments. Let's add comments to each step in our test class:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial &lt;/p&gt;

&lt;p&gt;import androidx.test.ext.junit.rules.activityScenarioRule &lt;br&gt;
import com.kaspersky.kaspresso.device.exploit.Exploit &lt;br&gt;
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.MainScreen &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen &lt;br&gt;
import org.junit.Rule &lt;br&gt;
import org.junit.Test &lt;/p&gt;

&lt;p&gt;class WifiSampleWithStepsTest : TestCase() { &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@get:Rule 
val activityRule = activityScenarioRule&amp;lt;MainActivity&amp;gt;() 

@Test 
fun test() { 
    // Step 1. Open target screen 
    MainScreen { 
        wifiActivityButton { 
            isVisible() 
            isClickable() 
            click() 
        } 
    } 
    WifiScreen { 
        // Step 2. Check correct wifi status 
        device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) 
        wifiStatus.hasEmptyText() 
        checkWifiButton { 
            isVisible() 
            isClickable() 
            click() 
        } 
        wifiStatus.hasText(R.string.enabled_status) 
        device.network.toggleWiFi(false) 
        checkWifiButton.click() 
        wifiStatus.hasText(R.string.disabled_status) 

        // Step 3. Rotate device and check wifi status 
        device.exploit.rotate() 
        wifiStatus.hasText(R.string.disabled_status) 
    } 
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
This slightly improves the readability of the code, but it won't solve all the issues. For example, if one of the tests fails, how do we know at which step it happened? Espresso in this case just dumps it into the log, so you have to examine the data, trying to figure out what went wrong. It would be much better if the logs displayed information about the start and end of each step.
We can also wrap some parts of the code in "try/catch" blocks to catch test failures. Now our test looks like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial &lt;/p&gt;

&lt;p&gt;import android.util.Log &lt;br&gt;
import androidx.test.core.app.takeScreenshot &lt;br&gt;
import androidx.test.ext.junit.rules.activityScenarioRule &lt;br&gt;
import com.kaspersky.kaspresso.device.exploit.Exploit &lt;br&gt;
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.MainScreen &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen &lt;br&gt;
import org.junit.Rule &lt;br&gt;
import org.junit.Test &lt;/p&gt;

&lt;p&gt;class WifiSampleWithStepsTest : TestCase() { &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@get:Rule 
val activityRule = activityScenarioRule&amp;lt;MainActivity&amp;gt;() 

@Test 
fun test() { 
    try { 
        Log.i("KASPRESSO", "Step 1. Open target screen -&amp;gt; started") 
        MainScreen { 
            wifiActivityButton { 
                isVisible() 
                isClickable() 
                click() 
            } 
        } 
        Log.i("KASPRESSO", "Step 1. Open target screen -&amp;gt; succeed") 
    } catch (e: Throwable) { 
        Log.i("KASPRESSO", "Step 1. Open target screen -&amp;gt; failed") 
        takeScreenshot() 
    } 
    WifiScreen { 
        try { 
            Log.i("KASPRESSO", "Step 2. Check correct wifi status -&amp;gt; started") 
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) 
            wifiStatus.hasEmptyText() 
            checkWifiButton { 
                isVisible() 
                isClickable() 
                click() 
            } 
            wifiStatus.hasText(R.string.enabled_status) 
            device.network.toggleWiFi(false) 
            checkWifiButton.click() 
            wifiStatus.hasText(R.string.disabled_status) 
            Log.i("KASPRESSO", "Step 2. Check correct wifi status -&amp;gt; succeed") 
        } catch (e: Throwable) { 
            Log.i("KASPRESSO", "Step 2. Check correct wifi status -&amp;gt; failed") 
        } 

        try { 
            Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -&amp;gt; started") 
            device.exploit.rotate() 
            wifiStatus.hasText(R.string.disabled_status) 
            Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -&amp;gt; succeed") 
        } catch (e: Throwable) { 
            Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -&amp;gt; failed") 
            takeScreenshot() 
        } 
    } 
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
In some catch blocks, we take screenshots that can help us analyze fails in the future. One way to take a screenshot is to call the takeScreenshot() method, but it is not recommended to use it directly. You can get acquainted with a more convenient and flexible tool for taking screenshots in Kaspresso [in this lesson](https://kasperskylab.github.io/Kaspresso/en/Tutorial/Logger_and_screenshot/).

The open-source Kaspresso framework offers Step, a useful and convenient abstraction. Everything that we have just written manually is implemented inside of it.

To use Steps, you need to call the run {} method and list in curly brackets all the steps that are to be run during the test. Each step should be called inside a Step block. The test code then looks like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial &lt;/p&gt;

&lt;p&gt;import androidx.test.ext.junit.rules.activityScenarioRule &lt;br&gt;
import com.kaspersky.kaspresso.device.exploit.Exploit &lt;br&gt;
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.MainScreen &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen &lt;br&gt;
import org.junit.Rule &lt;br&gt;
import org.junit.Test &lt;/p&gt;

&lt;p&gt;class WifiSampleWithStepsTest : TestCase() { &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@get:Rule 
val activityRule = activityScenarioRule&amp;lt;MainActivity&amp;gt;() 

@Test 
fun test() { 
    run { 
        step("Open target screen") { 
            MainScreen { 
                wifiActivityButton { 
                    isVisible() 
                    isClickable() 
                    click() 
                } 
            } 
        } 
        step("Check correct wifi status") { 
            WifiScreen { 
                device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) 
                wifiStatus.hasEmptyText() 
                checkWifiButton { 
                    isVisible() 
                    isClickable() 
                    click() 
                } 
                wifiStatus.hasText(R.string.enabled_status) 
                device.network.toggleWiFi(false) 
                checkWifiButton.click() 
                wifiStatus.hasText(R.string.disabled_status) 
            } 
        } 
        step("Rotate device and check wifi status") { 
            WifiScreen { 
                device.exploit.rotate() 
                wifiStatus.hasText(R.string.disabled_status) 
            } 
        } 
    } 
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Let's take a look at the logs. There we see both logs of specific actions and checks performed during the test, as well as meta-information about the steps - name, calling class, completion time, success message, or error log if the test failed.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xugk7awz232w3sbwzy7t.png)


Any user of the Kaspresso framework can add the necessary improvements to the functionality of the Steps: recording steps in the allure report, screenshots taken at the moment of step failure, view hierarchy output, and much more. To do this, the user needs to write their [own interceptor](https://kasperskylab.github.io/Kaspresso/en/Wiki/Kautomator-wrapper_over_UI_Automator/#interceptor-) or use a ready-made one.

Let's run the test again with the internet turned off:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqlr6xxf5a05cjgs49gx.png)

## Sections - preparing states before and after the test

Great, but there's something else to take into account. Often, when we compile tests, we have to consider the necessity of checking the application performance under certain preconditions. So in our test, we need the device to be in the default state before each launch, and to return to the same state after the test is completed.

To do this, there are before and after blocks in Kaspresso. The code inside the before block is run before the test - we can set the default settings here. During the test, the phone state may change: we can turn off the internet or change the orientation mode, but after the test, we need it to return to its original state. And that's where the after block comes in.

Let's improve our test class by using sections. Before starting the test in the before section, let's set the landscape mode and turn the Wifi on:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) &lt;br&gt;
device.network.toggleWiFi(true)&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
The before and after blocks help not only to visually separate the setup of the desired state into separate blocks but also to guarantee the completion of these blocks. If we look at the test that we wrote at the beginning of this article using Espresso, we can see that the same actions were recorded as steps, and if the test fails abruptly, the default settings may not be restored. Using before and after sections ensures that these blocks are run at the proper time.

Also note that now, after flipping the device, we check if the text remains the same, but not if the orientation mode has actually changed. It appears that if the device.exploit.rotate() method did not work for some reason, the orientation mode does not change and there is no point checking the text. Let's add a check to verify whether the orientation mode of the device has changed to landscape.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assert.assertTrue(device.context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Now the full test code looks like this:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package com.kaspersky.kaspresso.tutorial &lt;/p&gt;

&lt;p&gt;import android.content.res.Configuration &lt;br&gt;
import androidx.test.ext.junit.rules.activityScenarioRule &lt;br&gt;
import com.kaspersky.kaspresso.device.exploit.Exploit &lt;br&gt;
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.MainScreen &lt;br&gt;
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen &lt;br&gt;
import org.junit.Assert &lt;br&gt;
import org.junit.Rule &lt;br&gt;
import org.junit.Test &lt;/p&gt;

&lt;p&gt;class WifiSampleWithStepsTest : TestCase() { &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@get:Rule 
val activityRule = activityScenarioRule&amp;lt;MainActivity&amp;gt;() 

@Test 
fun test() { 
    before { 
        device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) 
        device.network.toggleWiFi(true) 
    }.after { 
        device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait) 
        device.network.toggleWiFi(true) 
    }.run { 
        step("Open target screen") { 
            MainScreen { 
                wifiActivityButton { 
                    isVisible() 
                    isClickable() 
                    click() 
                } 
            } 
        } 
        step("Check correct wifi status") { 
            WifiScreen { 
                wifiStatus.hasEmptyText() 
                checkWifiButton { 
                    isVisible() 
                    isClickable() 
                    click() 
                } 
                wifiStatus.hasText(R.string.enabled_status) 
                device.network.toggleWiFi(false) 
                checkWifiButton.click() 
                wifiStatus.hasText(R.string.disabled_status) 
            } 
        } 
        step("Rotate device and check wifi status") { 
            WifiScreen { 
                device.exploit.rotate() 
                Assert.assertTrue(device.context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 
                wifiStatus.hasText(R.string.disabled_status) 
            } 
        } 
    } 
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Conclusion and useful references
So, let's summarize. The open-source Kaspresso framework allows you to make test code readable due to a declarative approach using Kotlin DSL and an "out-of-the-box" implemented Page Object pattern, and it also allows you to make it stable due to internal improvements and interceptors. Here are some advantages of Kaspresso, highlighted by us using a specific example:

1. Good readability. You no longer need to use long constructs with matchers to search for elements on the screen for interaction from the test, and all the UI elements that your test interacts with can be described in one place (in a specific Screen object).
2. Logging. Kaspresso provides detailed and intuitive logs indicating the current step. You don't need to add them manually, because everything is implemented internally. If necessary, you can change and supplement the logs as you wish.
3. Code architecture. Using the Page Object pattern implementation described above, you can make your code in test files more readable, maintainable, reusable, and intuitive. Kaspresso also provides various methods and abstractions to improve the architecture (such as Steps, before and after sections, and more).
4. Low entry threshold for mastering the framework. No programming language knowledge is required to write autotests; all you need to do is use the declarative approach.

If this article was useful to you and you plan to use Kaspresso in your projects, join the [Kaspresso community on Discord](https://kas.pr/medium-nikitina-kaspresso-espressotests-discord). We will try to provide you with all the necessary support and announce the following articles from the current series of materials about Kaspresso. 
 
You can also take the [Tutorial](https://kas.pr/medium-nikitina-kaspresso-espressotests-tutorial) prepared by the Kaspresso team to familiarize yourself with other features of the framework and visit the [Wiki section](https://kasperskylab.github.io/Kaspresso/en/Wiki/). We hope to see you among our contributors soon! 
 
Furthermore, we welcome you to read our previous publications about autotests on Android: 
 
[A step-by-step tutorial in codelab format for Android UI testing ](https://medium.com/kasperskymedia/a-step-by-step-tutorial-in-codelab-format-for-android-ui-testing-a5581f251b29)
 
And don't forget to [give us a star on Github](https://kas.pr/medium-nikitina-kaspresso-espressotests-klgithub) ;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>tutorial</category>
      <category>android</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>A step-by-step tutorial in codelab format for Android UI testing</title>
      <dc:creator>Kaspresso</dc:creator>
      <pubDate>Fri, 16 Feb 2024 09:20:54 +0000</pubDate>
      <link>https://forem.com/kaspresso/a-step-by-step-tutorial-in-codelab-format-for-android-ui-testing-3a5k</link>
      <guid>https://forem.com/kaspresso/a-step-by-step-tutorial-in-codelab-format-for-android-ui-testing-3a5k</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xtkilh3otud85ts52lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xtkilh3otud85ts52lv.png" alt="Image description" width="720" height="374"&gt;&lt;/a&gt;&lt;br&gt;
Hi everyone! My name is Azamat Cherchesov and I am a senior Android developer at Kaspersky. We have been developing mobile applications for more than 10 years and know from our own experience that testing is an important stage in the entire process of creating new apps and preparing each new release. For Android applications Espresso is the default recommended framework, but it contains several known problems that do not allow us to cover all our needs for auto testing.&lt;/p&gt;
&lt;h1&gt;
  
  
  What do we want?
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Readability
&lt;/h2&gt;

&lt;p&gt;Espresso has some problems with readability. Its syntax is built on a hierarchical system of matchers, with a sufficiently high level of nesting, making the code hard to read and maintain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
fun espressoTest() {
  onView(withId(R.id.toFirstFeature))
    .check(ViewAssertions.matches(
     ViewMatchers.withEffectiveVisibility(
       ViewMatches.Visibility.VISIBLE)))
  onView(withId(R.id.toFirstFeature))
    .check(ViewAssertions.matches(ViewMatchers.isClickable()))
  onView(withId(R.id.toFirstFeature)).perform(ViewActions.click())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above is the simplest example, but even this shows a readability problem.&lt;/p&gt;

&lt;p&gt;For contrast, here’s an example of how this code could look differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
fun readableTest() {
  MainScreen {
    firstFeatureButton {
      isVisible()
      isClickable()
      click()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We would also like to have a declarative approach when writing autotests, which is also not possible with Espresso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stability
&lt;/h2&gt;

&lt;p&gt;Espresso does not work well with UI elements that are not displayed immediately. You can try &lt;a href="https://developer.android.com/training/testing/espresso/idling-resource"&gt;idling resources&lt;/a&gt;, but it is difficult to implement. Espresso tests are not stable — if you run a simple test several times it can fail once or twice for some unpredictable reason (a common problem for most testing frameworks). You can read more about unstable clicks in Espresso and how we improved it on our &lt;a href="https://kasperskylab.github.io/Kaspresso/en/Wiki/Kaspresso_configuration/#kakao-clicks"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detailed and easy-to-read logs and reports
&lt;/h2&gt;

&lt;p&gt;After testing, there is no logged sequence of actions that were successfully completed. In case of a fail, it can be difficult to understand at what step in a particular test script something went wrong — Espresso simply provides a long trace with an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Executing ADB commands
&lt;/h2&gt;

&lt;p&gt;Sometimes the default Android instrumental test tooling is not enough: a test case could imply that the device’s internet connection is off, brightness is set to a certain level, network is slow etc. Most of these conditions could be easily set beforehand on the CI side, but what if you want to turn off the internet during the test? Chances are you would need to fall back to manual testing. Here are some reasons why ADB may be needed in Android tests. In order to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;perform standard operations like push/pull, manipulate files, install additional APKs, uninstall unwanted, etc.&lt;/li&gt;
&lt;li&gt;emulate anything using ADB emu: call simulation, fingerprint interaction, slow internet simulation, set a fake geolocation, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interaction with other applications and system elements and interfaces
&lt;/h2&gt;

&lt;p&gt;You won’t even be able to interact with the system dialog using Espresso. Also you may need to perform some actions in another application during the test.&lt;/p&gt;

&lt;p&gt;Google recommends using UiAutomator, but its syntax is also not very readable and it is also different from Espresso. In addition, we would like to see much more stability and speed of operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;Mobile applications are often localized into multiple languages. Screenshot testing helps technical writers and localization engineers to better understand in what context new strings will be used and ensure they are suitable for the context and aren’t too long. It also helps with design testing — you can take real screens and compare them with mockups. We also want to see screenshots after running e2e tests (this can be useful when running tests using continuous integration to generate detailed reports).&lt;/p&gt;

&lt;p&gt;You may know some screenshot frameworks, but in addition to the native approach to writing screenshot tests, we would like to be able to take screenshots of the application in different states, including negative scenarios (internet lost, server returned an error, record not found in the database, subscription or license expired). To do this, we need an easy way to transfer the application (or some screens) to the required state directly from the test screenshot code without unnecessary external manipulations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kaspresso comes to the rescue
&lt;/h2&gt;

&lt;p&gt;As a result of much research and searching for best practices and solutions, the open source framework &lt;a href="https://github.com/KasperskyLab/Kaspresso"&gt;Kaspresso&lt;/a&gt; was created to cover all the needs for testing Android apps and has become a mature solution with a large team and an interested community. Take a look at some features Kaspresso can offer you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in protection against flaky tests&lt;/li&gt;
&lt;li&gt;Page object pattern out of the box&lt;/li&gt;
&lt;li&gt;Declarative approach for writing tests&lt;/li&gt;
&lt;li&gt;Ability to interact with other applications and system elements and interfaces&lt;/li&gt;
&lt;li&gt;Human readability with Kotlin DSL wrappers over UiAutomator and Espresso&lt;/li&gt;
&lt;li&gt;Detailed logs and reports (logs, view hierarchy, screenshots, video etc.)&lt;/li&gt;
&lt;li&gt;ADB support&lt;/li&gt;
&lt;li&gt;Screenshot testing with native approach (with dark mode support)&lt;/li&gt;
&lt;li&gt;Compose support&lt;/li&gt;
&lt;li&gt;Allure support&lt;/li&gt;
&lt;li&gt;Robolectric support&lt;/li&gt;
&lt;li&gt;Easy migration from Espresso&lt;/li&gt;
&lt;li&gt;Flexible configuration options&lt;/li&gt;
&lt;li&gt;Automatic artifact pulling after test execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find detailed information on our &lt;a href="https://kasperskylab.github.io/Kaspresso/en/"&gt;website&lt;/a&gt; in the Home and Wiki tabs.&lt;/p&gt;

&lt;h1&gt;
  
  
  A little dive into the details
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Protection against flaky tests
&lt;/h2&gt;

&lt;p&gt;Kaspresso provides interceptors and wrappers over Espresso and UiAutomator that catch exceptions in test code execution and retries with some additional logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ag97rid5r5jnnqg4q5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ag97rid5r5jnnqg4q5t.png" alt="Image description" width="720" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a nutshell, interceptors wrap calls to check and perform methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mjgml7tkw1y5w0x7dwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mjgml7tkw1y5w0x7dwv.png" alt="Image description" width="720" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Beyond simple retries, Kaspresso intercepts, and deals with, cases when scroll is needed or some elements are not visible due to system dialogs or long content loading.&lt;/p&gt;

&lt;p&gt;We recommend you try it now or learn more about it &lt;a href="https://kasperskylab.github.io/Kaspresso/en/Wiki/Kaspresso_configuration/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interaction with Android OS
&lt;/h2&gt;

&lt;p&gt;Kaspresso provides the Facade class Device. With this class you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;push/pull files&lt;/li&gt;
&lt;li&gt;enable/disable network&lt;/li&gt;
&lt;li&gt;give permissions like a user&lt;/li&gt;
&lt;li&gt;emulate phone calls and fake geolocations&lt;/li&gt;
&lt;li&gt;take screenshots&lt;/li&gt;
&lt;li&gt;enable/disable GPS&lt;/li&gt;
&lt;li&gt;rotate device or press system buttons&lt;/li&gt;
&lt;li&gt;enable/disable accessibility&lt;/li&gt;
&lt;li&gt;change app language&lt;/li&gt;
&lt;li&gt;collect and parse logcat output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(see more about the Device class &lt;a href="https://kasperskylab.github.io/Kaspresso/Wiki/Working_with_Android_OS/"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Readability
&lt;/h2&gt;

&lt;p&gt;First of all, let’s have a look at how beautiful, readable and concise Kaspresso tests are!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
fun readableTest() {
  MainScreen {
    firstFeatureButton {
      isVisible()
      isClickable()
      click()
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, Kaspresso uses &lt;a href="https://github.com/KakaoCup/Kakao"&gt;Kakao&lt;/a&gt;, — a Kotlin DSL wrapper over Espresso. From Kakao our framework inherited two main concepts.&lt;/p&gt;

&lt;p&gt;The first is KView, a special representation of the interface element with which interaction will occur in the test. Using KView saves us from constantly calling the onView method; now we just need to put matchers in the KView constructor once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;object MainScreen: Screen&amp;lt;MainScreen&amp;gt; {
   val firstFeatureButton = KButton{ withId(R.id.toFirstFeature) }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second concept is the Screen class (an implementation of the &lt;a href="https://kasperskylab.github.io/Kaspresso/en/Wiki/Page_object_in_Kaspresso/"&gt;page object pattern&lt;/a&gt;), which describes all the elements that will be interacted with during the test. The concept comes from web development and is about creating a description of the screen that is visible to the user. This object does not contain any logic, which allows you to describe screens and their elements in a separate file and interact with them from the test class code. Thus, page objects are completely independent, thereby achieving maximum reusability.&lt;/p&gt;

&lt;p&gt;Kotlin DSL over Espresso and UiAutomator provides a declarative approach for writing tests. Tests archives human readability and becomes easier for the next support and extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ability to execute ADB commands
&lt;/h2&gt;

&lt;p&gt;Espresso, unlike Appium, does not allow you to execute ADB commands. However, with Kaspresso you have the opportunity not only to execute ADB commands in native tests, but to do it directly from the test code. The implementation is not that simple. As I go into more detail in this article, I will make it more complex. See &lt;a href="https://proandroiddev.com/kaspresso-and-adb-server-7c35a80d8978?gi=a8d1c7db9444"&gt;this article&lt;/a&gt; for details of how this became possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;Kaspresso is not the first or only framework that allows you to create screenshot tests. But unlike other tools, our framework provides several killer screenshot features.&lt;/p&gt;

&lt;p&gt;Firstly, as we mentioned earlier, we want to be able to test (including screenshot tests) the application not only in positive, but also in negative scenarios (internet lost, server returned an error, record not found in the database, subscription or license expired). Because Kaspresso is a native framework, we can emulate any state inside the tests. Besides, having implemented Screen classes in regular E2e tests, you can reuse them and write screenshot tests very quickly.&lt;/p&gt;

&lt;p&gt;Secondly, Kaspresso allows you not only to set all the states by emulating various scenarios, but to do it directly without even having to launch the application! You can simply take the required Activity or Fragment and put it into the special holder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private lateinit var scenario: 
FragmentScenario&amp;lt;ScreenshotSampleFragment&amp;gt;

@ScreenShooterTest
@Test
fun test() = before {
  scenario = launchFragmentInContainer()
}.after {
}.run {
  scenario.onFragment {
    val view = getUiSafeProxy(it as ScreenshotSampleView)

    view.setNoInternetState()
    captureScreenshot("No Internet state")

    view.setContentLoadingState()
    captureScreenshot("Content is loading state")

    view.setServerErrorState()
    captureScreenshot("Server error state")
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, have a look at this &lt;a href="https://github.com/KasperskyLab/Kaspresso/blob/master/samples/kaspresso-sample/src/androidTest/kotlin/com/kaspersky/kaspressample/docloc_tests/advanced/AdvancedScreenshotSampleTest.kt"&gt;sample&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thirdly, Kaspresso allows you to change languages both at the system level and at the application level (by changing string resources in the application). This gives you more options and allows you to run tests faster&lt;/p&gt;

&lt;p&gt;Finally, Kaspresso provides the captureFullWindowScreenshot method for screenshotting long screens without having to scroll, as well as the &lt;a href="https://github.com/KasperskyLab/Kaspresso/blob/master/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/testcases/api/testcase/DocLocScreenshotTestCase.kt"&gt;DocLocScreenshotTestCase&lt;/a&gt; base class, which you can extend for writing screenshots tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detailed and understandable logs and reports
&lt;/h2&gt;

&lt;p&gt;Thanks to internal interceptors, Kaspresso wraps the test code, adding detailed logs indicating each step with screenshots. After running the tests, various artifacts are available (logs, screenshots, hierarchy of graphic elements, on-screen string identifiers, and much more, which can be downloaded automatically at the end of the run). For visual clarity, here is an example log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkz3k057gs0slgnfjips.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkz3k057gs0slgnfjips.png" alt="Image description" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Try the open source framework Kaspresso now with our step-by-step tutorial in codelab format
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgonlskpb8j5wjlf5zudk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgonlskpb8j5wjlf5zudk.png" alt="Image description" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We recently published &lt;a href="https://kasperskylab.github.io/Kaspresso/en/Tutorial/"&gt;14 lessons&lt;/a&gt; about Kaspresso to help you understand better and get real practice. All the lessons are based on the real and ready application, which you can run on your device or emulator (you need to download the source code from our &lt;a href="https://github.com/KasperskyLab/Kaspresso"&gt;GitHub&lt;/a&gt; and run the Tutorial project). If you experience problems during any lesson, you can switch to the &lt;a href="https://github.com/KasperskyLab/Kaspresso/tree/TECH-tutorial-results"&gt;TECH-tutorial-results&lt;/a&gt; branch and see the final implementation of all tutorial tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial structure
&lt;/h2&gt;

&lt;p&gt;The Tutorial is divided into steps (lessons). Each lesson begins with a brief overview and ends with a summary and conclusions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to study this tutorial?
&lt;/h2&gt;

&lt;p&gt;We strive to make the lessons independent from each other, but this is not always possible. For a better understanding of Kaspresso, we recommend starting with the first lesson and moving sequentially to the next. With the codelab format you will combine theory and practice, repeating the instructions from the lessons step by step. In the Kaspresso project, in the tutorial folder, there is an example of the application code for which tests will be written. The first lesson tells you how to download it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you need to know to complete the Tutorial?
&lt;/h2&gt;

&lt;p&gt;We are not trying to teach you autotests from scratch. At the same time, we set no restrictions on knowledge and experience for passing the tutorial, and keep the narrative understandable to autotest and Android beginners. It is almost impossible to talk about Kaspresso without terms from the Java and Kotlin programming languages, the Espresso, Kakao, UiAutomator and other frameworks, the Android operating system and testing itself as an IT area. Nevertheless, the main focus is on explaining Kaspresso itself, and wherever terms are mentioned, we share links to official sources for detailed information and better understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;If you find any typos, errors or inaccuracies in the materials, want to suggest an improvement or add new lessons to the Tutorial, you can &lt;a href="https://github.com/KasperskyLab/Kaspresso/issues/new"&gt;create an Issue&lt;/a&gt; in the Kaspresso project or open a Pull request (materials from the Tutorial are in the public domain in the &lt;a href="https://github.com/KasperskyLab/Kaspresso/tree/master/docs"&gt;docs&lt;/a&gt; folder).&lt;/p&gt;

&lt;p&gt;If the Tutorial did not answer your question, you can search the &lt;a href="https://kasperskylab.github.io/Kaspresso/Wiki/"&gt;Wiki&lt;/a&gt; section or &lt;a href="https://kasperskylab.github.io/Kaspresso/Home/Kaspresso-in-articles/"&gt;Kaspresso in articles&lt;/a&gt; and &lt;a href="https://kasperskylab.github.io/Kaspresso/Home/Kaspresso-in-videos/"&gt;Kaspresso in video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also join our &lt;a href="https://discord.com/invite/UYbEGWexXP"&gt;Discord channel&lt;/a&gt; and ask questions there.&lt;/p&gt;

&lt;h2&gt;
  
  
  I’m not saying goodbye
&lt;/h2&gt;

&lt;p&gt;As you can see, Kaspresso has a lot of very useful functionality that will help simplify your test-writing process. The structured step-by-step tutorial in codelab format lets you not only study this framework, but learn how to write autotests in general.&lt;/p&gt;

&lt;p&gt;We will publish a series of articles here, so follow our blog.&lt;/p&gt;

&lt;p&gt;Detailed info on how to try out Kaspresso is available on our &lt;a href="https://github.com/KasperskyLab/Kaspresso"&gt;GitHub&lt;/a&gt; page and &lt;a href="https://kasperskylab.github.io/Kaspresso/en/#integration"&gt;website&lt;/a&gt;. Give stars to our project on GitHub and join our &lt;a href="https://discord.com/invite/UYbEGWexXP"&gt;Discord community&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>testing</category>
      <category>android</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
