Instrumentation Testing: Testing individual composables - devrath/RunTracer GitHub Wiki

Testing individual composables

  • Instrumentation testing for individual composables in Jetpack Compose provides a robust framework for ensuring UI correctness, improving code quality, facilitating refactoring, and enhancing the user experience.
  • By automating these tests, you can save time, reduce manual effort, and maintain a high standard of reliability and performance in your Android applications.

Dependencies

dependencies {
    // For Android Instrumentation tests
    androidTestImplementation 'androidx.test:runner:1.4.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    
    // For Compose testing
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.0.0'
    debugImplementation 'androidx.compose.ui:ui-test-manifest:1.0.0'
}

Approach

  • We shall use two classes one for test and another as robot.
  • Thus we shall use the ROBOT PATTERN.

Code

TestClass

class RegisterScreenKtTest {

    @get:Rule
    val composeRule = createComposeRule()

    private val robot = RegisterScreenRobot(composeRule)

    @Test
    fun testRegisterScreen_whetherInitialUiIsLoadedProperly() {
        robot.apply {
            loadRegisterScreen()
            validateUiLoaded()
        }
    }

    @Test
    fun testRegisterScreen_inputIsValidEmailEntered() {
        robot.apply {
            loadRegisterScreen()
            inputValidEmail()
            validateEnteredEmail()
        }
    }

    @Test
    fun testRegisterScreen_inputIsValidPasswordEntered() {
        robot.apply {
            loadRegisterScreen()
            inputValidPassword()
            validateEnteredPassword()
        }
    }

}

RobotClass

internal class RegisterScreenRobot(private val composeRule: ComposeContentTestRule) {

    private val context = InstrumentationRegistry.getInstrumentation().context

    private val createAccountText = context.getString(R.string.create_account)
    private val atLeastOneNumber = context.getString(R.string.at_least_one_number)
    private val containsLowerCharacter = context.getString(R.string.contains_lowercase_char)
    private val containsUpperCharacter = context.getString(R.string.contains_uppercase_char)
    private val registerText = context.getString(R.string.register)
    private val emailText = context.getString(R.string.email)
    private val passwordText = context.getString(R.string.password)

    val emailInput = "[email protected]"
    val passwordInput = "Test@123"

    /**
     * Loading and setting the register screen
     */
    fun loadRegisterScreen() {
        composeRule.setContent {
            RunTracerTheme {
                RegisterScreen(
                    state = RegisterState(
                        passwordValidationState = PasswordValidationState(
                            hasNumber = true
                        )
                    ),
                    onAction = {}
                )
            }
        }
    }

    /**
     * Check if all the UI elements are properly loaded
     */
    fun validateUiLoaded() {
        composeRule.apply {
            onNodeWithText(createAccountText).assertIsDisplayed()
            onNodeWithText(atLeastOneNumber).assertIsDisplayed()
            onNodeWithText(containsLowerCharacter).assertIsDisplayed()
            onNodeWithText(containsUpperCharacter).assertIsDisplayed()
            onNodeWithText(registerText).assertIsDisplayed()
        }
    }

    /**
     * ********************* Adding inputs *********************
     */
    fun inputValidEmail() {
        composeRule.onNodeWithTag(emailText).assertExists()
        composeRule
            .onNodeWithTag(emailText)
            .performTextInput(emailInput)
    }

    fun inputValidPassword() {
        composeRule.onNodeWithTag(passwordText).assertExists()
        composeRule
            .onNodeWithTag(passwordText)
            .performTextInput(passwordInput)
    }
    /**
     * ********************* Adding inputs *********************
     */


    /**
     * ********************* Validating inputs *********************
     */
    fun validateEnteredEmail() {
        composeRule.onNodeWithText(emailInput).assertIsDisplayed()
    }

    fun validateEnteredPassword() {
        composeRule.onNodeWithText(passwordText).assertIsDisplayed()
    }
    /**
     * ********************* Validating inputs *********************
     */

}