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 developers and testers, which facilitates and speeds up the process of automating the testing of mobile applications on the Android platform.
Kaspresso is based on Espresso and UI Automator and provides a wide range of additional functions, such as:
Let's get started with the project. First, you need to add dependencies to the build.gradle
file:
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 androidTest
folder
We create the MainScreen
class and inherit it from KScreen
. So, we follow the Page Object approach.
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 layoutId
and viewClass
in our case, it is R.layout.activity_main
and 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 Kotlin DSL
over Espresso
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 KButton
class. We initialize it with a block, inside which we call a function to search for the widget using the id
of the desired button - withId(R.id.add_button)
.
Then, we create the clickAddNote
method and add a call to the click()
method on the element.
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 KRecyclerView
for interaction with RecyclerView
. In KRecyclerView's builder, we determine by which id to find the RecyclerView
.
itemTypeBuilder
determines what elements our RecyclerView
consists of. NoteItem
is our class which is used to describe a list item i.e. elements of View Holder.
Now, let's write a test for the adding a note screen. Here, we are already using the input field, and we need the KEditText
class, to enter text we call the typeText
method. At the end, we call the pressBack
method. The first time is needed to hide the keyboard, and the second time to close the screen, and when closing, notes are saved.
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 NoteTest
by inheriting from TestCase
and need to add a rule that provides control over the activity MainActivity
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.
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 @Test
annotation. The first step is going to the page and adding a note. The next step is adding a new note using the NoteDetailScreen.inputNoteData
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 hasAnyText
method, and at the end call deleteIcon
to click.
Finally, we run the test and wait for the result!
Links: