Understanding unit testing in Android with Jetpack and Kotlin

Unit testing in Android with Jetpack and Kotlin gives end users of a software product great user experiences .Additionally,it assists in the debugging of functionality issues at an early stage.

· 8 min read
Wilson Ochieng

Wilson Ochieng

Android Expert developing mobile applications with 4+ Years of Experience.

topics

Introduction

It is important to guarantee code reliability and maintainability in the fast-paced world of Android application development. Unit testing is essential in accomplishing these objectives since it confirms that individual code units are valid. The world of Android UI development has changed with the introduction of Jetpack Compose and Kotlin, which has introduced new perspectives and approaches for unit testing.

The Evolution of Android UI Development

Imperative programming concepts and XML-based layouts have historically been the main tools used in Android User Interface development. Nevertheless,software engineers can now use Kotlin to create declarative user interfaces by leveraging Jetpack Compose. This change offers a fresh method for unit testing in addition to streamlining UI development.

Using Jetpack to Increase Testability

Testability is one of Jetpack Compose's main benefits. Compose UI elements are very modular and simple to test because they are represented as composable functions. These modular functions can be called directly by unit tests, which pass in test data and assert the desired results. With this much detail, software developers may create targeted tests to verify the integrity of specific UI elements separately.

Using Kotlin to Write Unit Tests

The unit testing experience is further enhanced by Kotlin's expressiveness and conciseness. Kotlin's characteristics, such as lambdas, higher-order functions, and extension functions, can be utilized by software engineers to create readable and organized test code. Furthermore, Kotlin's type inference and null safety features aid in identifying possible mistakes during compilation, lowering the possibility of test runtime problems.

Testing approaches for Jetpack

Some of the key strategies when implementing unit testing in Kotlin and Jetpack Compose include:

Testing at the Component Level

Mocking the dependencies that each separate composable function depends on, test each one separately. This method ensures that every UI element functions as it should in various situations.

Integration Testing

Examine the interaction among various modular components to confirm the general functionality of user interface screens. Integration tests assist in identifying problems that might occur from the arrangement of different UI components.

State Testing

Verify how UI elements behave when their states change or when they receive input events. Developers can make sure that their user interface (UI) is responsive and consistent by modeling various user interactions and states.

Benefits of Unit Testing

Faster Development

By using automated testing processes, developers are able to quickly identify any regressions and make sure that any new changes do not accidentally interfere with the functionality that is currently in place.

Early bug Detection

Unit testing allows software developers to methodically test individual UI code units in isolation, which helps with early bug detection in Jetpack. With this method, some features or behaviors inside the UI elements can be verified without depending on the full integration of the program. Engineers can check expected outputs and interactions by simulating different situations and inputs and implementing unit tests for the components. By using this procedure, any irregularities, strange behaviors, or problems can be found early in the development cycle and fixed before they spread across the entire application.

Better Quality of Code

When you write unit tests for a piece of code,the code will be highly modularized and maintainable.On the other hand,a less modularized code will be very hard to maintain for most developers.

Kotlin Test Libraries

Kotlin has a number of test libraries consisting of test frameworks,mocking and assertions.This leaves developers with many options to choose their preferred ones.The test frameworks include Spectacular framework that leverages on Mockito-Kotlin for mocking and uses assertions such as Strikt,Atrium and HamKrest.Additionally,JUnit framework is one of the most used testing frameworks in both Kotlin and Java.The tests in this tutorial will be written in JUnit4.

Unit Test in Action

To implement unit testing in an application,ensure that the required dependencies are added in the build.gradle.kts file.In addition,you can leverage other libraries such as expresso or mockito to do UI testing.

androidTestImplementation "androidx.compose.ui:ui-test-junit4:$version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$rootProject.composeVersion"

Compose Version above denotes the latest compose version for instance 2.2.0.

You can test any feature in an application to determine exactly which UI components work correctly.In the example below you will test if a tab displays a label only if selected.Additionally,you will determine if the active screen defines the tab that is selected.All tests are conventionally written inside files located in androidTest package.

Image

1.To begin testing,create a new file and name it MyTestFile.kt inside the androidTest package.You can then use ComposeTestRule,a test rule that comes with compose by calling createComposeRule().


package com.example.compose.rally
class MyTestFile {

@get:Rule
    val composeTestRule = createComposeRule()
     
}

The testing above can be done by exploring Espresso library when using Android View.

@get:Rule
val composeTestRule = createAndroidComposeRule(RallyActivity::class.java)

However,since Jetpack compose was introduced,things have been made simpler.

2.To test the RallyTopAppBar found in the ui/components folder with code below call it within MyTestFile.The code below has @Rule annotation that defines a JUnit rule.Additionally,@Test annotation is the JUnit test method.Inside the test method,there is a block called composeTestRule.setContent { } that takes three parameters allScreens, onTabSelected and currentScreen.Additionally,there is a finder method composeTestRule.onNodeWithContentDescription(RallyScreen.Accounts.name) that does a search for the content description from RallyScreen.Accounts.name.


package com.example.compose.rally
import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import com.example.compose.rally.ui.components.RallyTopAppBar
import org.junit.Rule
import org.junit.Test

class MyTestFile {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun rallyTopAppBarTest() {
        val allScreens = RallyScreen.values().toList()
        composeTestRule.setContent {
            RallyTopAppBar(
                allScreens = allScreens,
                onTabSelected = { },
                currentScreen = RallyScreen.Accounts
            )
        }
        composeTestRule
            .onNodeWithContentDescription(RallyScreen.Accounts.name)
            .assertIsSelected()
    }
}

RallyTopAppBar.kt

package com.example.compose.rally
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.example.compose.rally.ui.components.RallyTopAppBar
import com.example.compose.rally.ui.theme.RallyTheme

class RallyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            RallyApp()
        }
    }
}

@Composable
fun RallyApp() {
    RallyTheme {
        val allScreens = RallyScreen.values().toList()
        var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) }
        Scaffold(
            topBar = {
                RallyTopAppBar(
                    allScreens = allScreens,
                    onTabSelected = { screen -> currentScreen = screen },
                    currentScreen = currentScreen
                )
            }
        ) { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                currentScreen.content(onScreenChange = { screen -> currentScreen = screen })
            }
        }
    }
}


3.When you right click rallyTopAppBarTest() to run the tests then you should be able to see tests that have passed and those that have failed.The output of the test results is shown below.

Image

Unit Testing Best Practices

Apply Descriptive Names

To make your test procedures and classes more comprehensible, give them clear and concise names.

Use conventional patterns

Make use of verbs such as Arrange, Act and Assert to make the testing process clearer and flawless.

Modularized testing

Test each module independently and in isolation to ensure that tests are written for every line of code.

Automate Test runs

Automate test runs by implementing continuous integration tools to enable unit testing evert time code is committed.

Conclusion

Android unit testing provides a concise means of guaranteeing code stability and quality, particularly when used in conjunction with Jetpack Compose and Kotlin.Software developers may create reliable and manageable tests for their Android applications by using declarative UI development and taking advantage of Kotlin's expressive features. Teams can increase their trust in the performance and dependability of their applications by concentrating on component-level testing, integration testing, and state testing. This will ultimately result in a flawless user experience.






share

Wilson Ochieng

Android Expert developing mobile applications with 4+ Years of Experience.