Kaspresso is a powerful framework for automating the testing of mobile applications on the Android platform. One of the key features of Kaspresso is its user-friendly and intuitive architecture. Thanks to the modular approach, developers can easily customize and modify test scripts and take different approaches to creating test cases. Kaspresso libraries allow you to effectively interact with UI elements, perform actions, and test expected results. Thus, Kaspresso is an incredibly useful tool for and testers, which facilitates and speeds up the process of automating the testing of mobile applications on the Android platform. developers Kaspresso is based on and and provides a wide range of additional functions, such as: Espresso UI Automator 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 test execution Significantly faster execution of UI Automator commands. With Kaspresso, some UI Automator commands run ! 10 times faster Page object pattern from the box Let's get started with the project. First, you need to add dependencies to the file: build.gradle androidTestImplementation 'com.kaspersky.android-components:kaspresso:1.5.3' For example, I’ll take a ready-made test Notes application. Where it will be possible to add, change, and delete notes. We will write the test in the folder androidTest We create the class and inherit it from . So, we follow the Page Object approach. MainScreen KScreen The Page Object approach implies that the modeled class will completely describe one screen of the application under test - all screen elements and methods for interacting with these elements. This way, we won't have to re-declar the same elements every time in our autotests. Next, we redefine and in our case, it is and : layoutId viewClass R.layout.activity_main MainActivity::class.java object MainScreen: KScreen<MainScreen>() { private val buttonAddNote = KButton{ withId(R.id.add_button) } fun clickAddNote(){ buttonAddNote.click() } override val layoutId: Int = R.layout.activity_main override val viewClass: Class<*> = MainActivity::class.java } Kaspresso uses the convenient over to interact with elements provided by the Kakao library. Many ready-made wrappers have already been created for standard UI widgets. Let's use one of them to find a button - we need the class. We initialize it with a block, inside which we call a function to search for the widget using the of the desired button - . Kotlin DSL Espresso KButton id withId(R.id.add_button) Then, we create the method and add a call to the method on the element. clickAddNote click() Let's also add a test for the list of notes to . MainScreen object MainScreen: KScreen<MainScreen>() { private val buttonAddNote = KButton{ withId(R.id.add_button) } val noteRv = KRecyclerView( builder = { withId(R.id.notes_rv) }, itemTypeBuilder = { itemType(::NoteItem) } ) fun clickAddNote(){ buttonAddNote.click() } override val layoutId: Int = R.layout.activity_main override val viewClass: Class<*> = MainActivity::class.java class NoteItem(matcher: Matcher<View>) : KRecyclerItem<NoteItem>(matcher) { val deleteIcon = KImageView(matcher) { withId(R.id.delete_icon) } val tvNoteTitle = KTextView(matcher) { withId(R.id.title_tv) } val tvNoteText = KTextView(matcher) { withId(R.id.notes_tv) } } } Added for interaction with . In KRecyclerView's builder, we determine by which id to find the . determines what elements our consists of. is our class which is used to describe a list item i.e. elements of View Holder. KRecyclerView RecyclerView RecyclerView itemTypeBuilder RecyclerView NoteItem Now, let's write a test for the adding a note screen. Here, we are already using the input field, and we need the class, to enter text we call the method. At the end, we call the method. The first time is needed to hide the keyboard, and the second time to close the screen, and when closing, notes are saved. KEditText typeText pressBack object NoteDetailScreen : KScreen<NoteDetailScreen>() { private val titleEd = KEditText{ withId(R.id.title_et) } private val noteEd = KEditText{ withId(R.id.note_et) } fun inputNoteData(title: String, note: String) { titleEd.typeText(title) noteEd.typeText(note) pressBack() pressBack() } override val layoutId: Int get() = R.layout.fragment_notes_detail override val viewClass: Class<*> get() = NotesListFragment::class.java } We create by inheriting from and need to add a rule that provides control over the in the autotest. This is necessary to be able to interact with the interface of this activity in the application, in our case this is our main screen. NoteTest TestCase activity MainActivity class NoteTest : TestCase() { @get:Rule val activity = activityScenarioRule<MainActivity>() @Test fun noteTest() = run { step("Navigate to add note screen") { MainScreen.clickAddNote() } step("Add new note and back") { NoteDetailScreen { inputNoteData("Title first", "Note text") } } step("Delete first note") { MainScreen { noteRv { childAt<MainScreen.NoteItem>(0) { tvNoteTitle.hasAnyText() tvNoteText.hasAnyText() deleteIcon.click() } } } } } } Next, we create the noteTest method with the annotation. The first step is going to the page and adding a note. The next step is adding a new note using the method. The last step is to contact the RecyclerView and get the first element of the list, check whether the element has at least some text using the method, and at the end call to click. @Test NoteDetailScreen.inputNoteData hasAnyText deleteIcon Finally, we run the test and wait for the result! Links: Notes project with Kaspresso Kaspresso