Automated Android Testing With Kotlin
Learn how to write UI and unit tests in Kotlin with the Espresso and JUnit frameworks in Android Studio, and related app development techniques.
Join the DZone community and get the full member experience.
Join For FreeAs of Android Studio Version 3.0, Kotlin is officially joining the Android language family. However, we don’t want to lose our ability to test our app by using a different language. Luckily, we can use our Java knowledge and many new techniques to test our apps in Kotlin.
After reading this article, you should be able to:
- Set up Kotlin in Android Studio.
- Write unit tests in Kotlin.
- Write UI tests in Kotlin.
- Use extension functions.
Why Kotlin?
Kotlin’s simple and elegant. If you have a Java background, you’re ready to develop in Kotlin. Some of the best Kotlin features are:
- 100% compatible with Java and all of its libraries.
- Extension functions.
- Awesome frameworks for Android development.
Of course, we will talk about all of these features later on.
Why Should We Create Tests?
Testing should be an integral part of every developer’s routine. If you release an app, there should be no bugs left. You definitely don’t want your users to find bugs because this will damage your reputation as a developer. This alone should be more than enough motivation for you to start testing if you haven’t done so already.
So, let’s learn how to set up Kotlin in Android Studio.
Set Up Kotlin in Android Studio
Since Kotlin is a language developed by JetBrains, the support for Kotlin in Android Studio is superb. First off, you’ll have to install the Kotlin Plugin in Android Studio. To do this, just install it in the File > Settings > Plugins > Install JetBrains plugin...dialog. Now, we’ll need to import or create a Java app.
After you’ve done this, you have to convert the Java files into Kotlin files. Just open a Java file and press Ctrl + Alt + Shift + K. By the way, Java and Kotlin can work side-by-side, so if you don’t want to convert all your files, that should work just fine.
Right now, Android Studio will probably tell you that Kotlin isn’t configured. To do this, just press Configure > Android with Gradle > OKand sync Gradle. Voilà, you are ready to develop in Kotlin.
Our Example App
Before we can start testing an app, we actually have to create one. For this guide, we will test an app that takes the name of the user and creates a greeting. The app consists of two input views, one output view, and a button. You can find the finished project on GitHub.
We use one input view for the first name and one for the last name. When you click the button, the message Hello $firstName $lastName!
will appear in the output view. Underneath the surface, the message will be computed by a function fun greeting(firstName: String, lastName: String): String
, which we’re going to test later on.
In the next section, we are going to create unit tests for this app.
Unit Tests
Unit tests check the concrete functions and their output but don’t interact with the UI. The default framework for writing unit tests is Android’s JUnit, so we’re going to use it too.
Our app has two test cases that need to be checked. First, we have to test that our greeting()
function returns the right message if the first and last name parameters aren’t empty. Second, we want to check that the same method returns an empty string if the first or last name is empty.
With this in mind, let’s look at the default Android Studio unit test class.
Our Default Test Class
After we convert the default unit test class into Kotlin, it should look something like this:
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, (2 + 2).toLong())
}
}
As you can see, unit tests in Kotlin look like the ones in Java so that we can reuse and extend our knowledge. As we’ve discussed earlier, we have two cases we have to test.
Without further ado, let’s get to testing.
Our Unit Tests
Check Normal Behavior and Extend JUnit
class ExampleUnitTest {
@Test
fun testNormalGreeting() {
greeting(firstName = “first”, lastName = “last”)
equals “Hello first last!”
}
}
infix fun Any?.equals(o2: Any?) = assertEquals(this, o2)
If you’ve written any unit tests before, this should look pretty familiar to you. We check greeting()
with a first and last name and test whether or not greeting()
returns our desired message.
We’ve also added an extension function. It helps us to make our code more readable by hiding redundant code.
The extension function converts assertEquals()
into an infix function. You could do the same, for example, for assertThat()
or assertArrayEquals()
. Next, let’s test greeting()
with empty parameters.
Check Empty Parameters
class ExampleUnitTest {
//...
@Test
fun testEmptyFirstName() {
greeting(firstName = “”, lastName = “last”) equals “”
}
@Test
fun testEmptyLastName() {
greeting(firstName = “first”, lastName = “”) equals “”
}
}
//...
When you look at this case, you shouldn’t forget that we have two parameters that can be empty.
Nevertheless, for both tests, the greeting()
should return an empty message.
And that’s it for our unit tests. We created all the necessary tests for greeting()
. Now, we can close our unit test files and step right into UI tests.
UI Tests
Up until now, we have just created tests for the internal mechanisms inside our app. Unfortunately, if our UI’s broken, unit tests won’t warn us. They don’t rely on the UI, but users do! For this reason, we have to test it. UI tests interact with the UI, not with the underlying method calls. They check that everything works hand-in-hand when the user interacts with the app.
We will use the Espresso framework for our UI tests. It’s already part of our app and works really well.
How To Set Up Espresso
As you can see, there isn’t only a default test class for unit tests but a class for integration tests, too! The latter one can be used for our UI tests. However, we need to prepare the class for Espresso by adding a rule. Otherwise, our tests will fail.
In the end, it should look something like this:
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@Rule @JvmField val activity =
ActivityTestRule<MainActivity>(MainActivity::class.java)
@Test
//...
}
Next, we can consider for which cases we have to create UI tests.
Our UI Tests
The test cases are still the same as in our unit tests, but this time, we are going to check the UI. UI tests are really easy to read, so even if you haven’t seen any UI tests before, you will understand the following example:
Check Normal Behavior
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@Rule @JvmField val activity =
ActivityTestRule<MainActivity>(MainActivity::class.java)
@Test
@Throws(Exception::class)
fun testNormalGreeting() {
val firstName = “first”
val lastName = “lastName”
val expected = “Hello $firstName $lastName!”
onView(withId(R.id.firstNameView)).
perform(typeText(firstName))
onView(withId(R.id.lastNameView)).
perform(typeText(lastName))
R.id.messageButton.click()
onView(withId(R.id.messageView)).
check(matches(w ithText(expected)))
}
}
fun ID.click() = onView(withId(this)).
perform(ViewActions.click()
And here’s our very first Espresso test! This test writes the first and last name into the input view, presses the button, and checks that our output view contains the right message. As you can see, the extension function can help us create UI tests too. In the next example, we will use many more.
Check Empty Input Views
If one of the input views is empty, the output view should remain empty. However, we also want to display a Toast that tells the user what went wrong. Because a Toast lacks an ID, we have to find it by its message. This message is: You forgot to write your full name!
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
//...
@Test
@Throws(Exception::class)
fun testEmptyFirstName() {
R.id.firstNameView.write(“”)
R.id.lastNameView.write(“last”)
R.id.messageButton.click()
R.id.messageView.textEquals(“”)
activity containsToast “You forgot to write your full name!”
}
@Test
@Throws(Exception::class)
fun testEmptyLastName() {
R.id.firstNameView.write(“first”)
R.id.lastNameView.write(“”)
R.id.messageButton.click()
R.id.messageView.textEquals(“”)
activity containsToast “You forgot to write your full name!”
}
}
(You can find all extension functions on GitHub.)
As you can see, most Espresso methods consist of two parts:
- Find the view you need.
- Do something with this view.
You have many, many choices for both parts. But all of them work in the same way. You can find more methods on this site.
Next Steps
Now, you know the basic concepts of how to test your app in Kotlin. You know how to set up Kotlin in Android Studio, how to write unit tests with JUnit and UI tests with Espresso, and how to simplify them by using extension functions.
But there are many techniques left to learn. You can learn how to use functional programming, UIAutomator, Kotlinx, and KotlinTest in your tests. And always keep in mind that if your Kotlin code doesn’t look good, there’s probably a better way.
Published at DZone with permission of Niklas Wuensche, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments