Get started with Espresso & Kotlin!
Start a UI testing project from scratch using those two tools
This article provides information to start a UI testing project from scratch based on Kotlin and Espresso. In the following example, we assume the app has been developed in Java and needs a specific configuration to develop and execute tests in Kotlin.
Espresso is a native framework for UI automated testing. It comes with the Android SDK and presents many features that make this tool more reliable when executing UI tests against native apps.
Espresso supports two languages for the moment: Java and Kotlin. This time we will go through everything needed to set up the basics for executing test cases under Kotlin configuration.
The following image represents what we commonly get at the time of building an Android application:
This example is based on an Android application created in Java using Kotlin for testing.
The package com.example.intend (androidTest)
will hold the UI tests performed against the application and com.example.intend (test)
holds the unit tests.
Writing the first test
We will create our first test in Kotlin and locate it in com.example.intend (androidTest)
.
Let’s call the class: KotlinExample
. Once we create that class, we should create our first function and declare the kind of execution we’ll be performing. To do this, we will use Junit and its decorator @RunWith()
, which will allow us to define the runner for this class.
@RunWith(AndroidJUnit4::class)
class KotlinExample {
@Test
fun modifyText() {
}
}
Right now, the test function seems ready to be executed. Probably you won’t get any IDE compiling warning, but at the time of executing this function, you’ll be getting the following error:
Didn't find class "com.example.intend.KotlinExample" on path: DexPathList…….
This is expected since we have yet to add our Kotlin dependencies. First, we need to add a Kotlin plugin for Gradle to our build.gradle
(Project: <Project Name>)
file as follows:
buildscript {
ext.kotlinVersion = "1.7.10"
ext.agpVersion = "7.3.1"
repositories {
// Insert local test repo here
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:$agpVersion"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here;
// they belong in the individual module build.gradle files
}
}
Add the following two plugins in the build.gradle (Module: <Project name>.app)
file:
import com.android.build.api.dsl.ManagedVirtualDevice
apply plugin: 'com.android.application'
//Plugins for handling kotlin code
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
task wrapper(type: Wrapper){
gradleVersion = '7.5.1'
}
android {
compileSdkVersion 33
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
applicationId "com.example.intend.BasicSample"
minSdkVersion 14
targetSdkVersion 33yes.
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Make sure the following dependencies are already added in the same file:
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
}
Now you should be able to execute your test function (Right click on the function name and click run).
Let’s continue writing our first scenario. Our application is a simple app that allows us to write a text and change the title on the same page or place it on a second screen:
Element Inspection
For this example, we are going to rely on Android studio’s layout inspector, which you can access via Tools->Layout Inspector
and select the app Id you want to inspect.
So we have a couple of sections in the inspector to see the components’ tree and the details about the components:
It’s a very easy-to-use inspector and shows the basics for this example. As you can see, the id for the Input in the first screen of the app under test is editTextUserInput. So we are going to use that to be able to change the value for the input through our test.
First, we need to add a rule to our tests to launch the app and close it after every test. To do that, we need to add the following dependencies to our build.gradle(Module: <Project name>)
file.
// Testing-only dependencies
androidTestImplementation 'androidx.test.ext:junit:'
+ rootProject.extJUnitVersion
androidTestImplementation 'androidx.test.ext:junit-ktx:'
+ rootProject.extJUnitVersion
And then add the rule to the test class:
@RunWith(AndroidJUnit4::class)
class KotlinExample {
@get:Rule
var activityScenarioRule = activityScenarioRule<MainActivity>()
@Test
fun modifyText() {
}
}
Then we need to start dealing with the app objects. In this case, we’ll be adding certain text to the input component: editTextUserInput, then clicking on the button to change the text in the main screen, and finally verifying the text was changed:
companion object {
val STRING_TO_BE_TYPED = "Espresso"
}
@Test
fun modifyText() {
onView(withId(R.id.editTextUserInput))
.perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard())
onView(withId(R.id.changeTextBt)).perform(click())
onView(withId(R.id.textToBeChanged))
.check(matches(withText(STRING_TO_BE_TYPED)))
}
We have many functions in Espresso that can apply to different objects in our application. You can find a very user-friendly list of actions you can perform with Espresso in the following link: https://developer.android.com/training/testing/espresso/cheat-sheet
POM (Page Object Model) in Espresso Tests
You can also apply any design pattern you might consider suitable for your project, such as POM. We can create a couple of pages to migrate this test to POM. Let’s call them MainScreen and BasePage
. MainScreen
is going to control the objects and actions in the Main Screen and BasePage, and the common actions across screens:
class MainScreen : BasePage() {
private val editTextUserInput = R.id.editTextUserInput
private val changeTextBt = R.id.changeTextBt
val textToBeChanged = getView(R.id.textToBeChanged)
fun enterInputValue(value: String): MainScreen {
enterText(editTextUserInput, value)
return this
}
fun clickChangeText(): MainScreen {
clickOn(changeTextBt)
return this
}
}
open class BasePage {
fun enterText(id:Int, value:String){
onView(withId(id))
.perform(typeText(value))
}
fun clickOn(id:Int){
onView(withId(id))
.perform(click())
}
fun getView(id:Int): ViewInteraction? {
return onView(withId(id))
}
}
Now we create another test using the POM we just created:
@Test
fun modifyTextPO() {
val mainScreen = MainScreen()
mainScreen.enterInputValue(STRING_TO_BE_TYPED)
.clickChangeText()
mainScreen.textToBeChanged?
.check(matches(withText(STRING_TO_BE_TYPED)))
}
Execution
For test execution, we are using Android studio. We can just go to our run/debug configuration and create a new Android instrumented tests configuration based on our app and current test:
Also, you can automatically get the test configuration by right-clicking on the function and running the test execution. For command line execution, we can do it through the Gradle wrapper: Gradlew.
Add to the build.gradle (Module: <Project Name>.app)
file the following task:
task wrapper(type: Wrapper){
gradleVersion = '7.5.1'
}
Specify the version of Gradle you are using. Then, execute the following command to get the Gradle wrapper: gradle wrapper — gradle-version 7.5.1
. Once we have the wrapper set up, we can go ahead and execute the following command to execute our specific Test:
./gradlew connectedDebugAndroidTest -P android.testInstrumentationRunnerArguments.class=com.example.intend.KotlinExample#modifyTextPO;
Or execute by class name:
./gradlew connectedDebugAndroidTest -P android.testInstrumentationRunnerArguments.class=com.example.intend.KotlinExample
Reporting
After execution, a default HTML report is generated by Gradle under app/build/reports/androidTests/connected
directory. You will find the index.html file, which contains a summary of the results:
The reports above can be customized as it’s shown in the following image, including a screenshot, for example, or any data you might consider important to report:
We can manage to customize this report or simply add another tool to get a more detailed report if needed.
Conclusion
Espresso is a very powerful and reliable tool to use for Android UI test automation. Espresso allows users to work without a proxy between the app and the test code making execution faster and more reliable. It’s up to you to choose the right tools for automation. I hope this is useful for you and helps you get started.