Topic: 20 Understanding Testing in Android

This is our twentieth topic from learn android from basic to advance series

Β·

10 min read

Topic: 20 Understanding Testing in Android

Hello devs, Can you believe we've reached the last topic in our Learn Android from Basic to Advance series? It's been quite a journey! Ok, devs So whenever you write a code for your Android App always remember to follow a test-driven development approach. Write a test case for your code. Let's Explore TDD.

TDD- Test Driven Development

Test-driven development (TDD) is like building a puzzle: you start with a picture in mind (your test), then you put in one piece at a time (your code) until the whole puzzle fits together perfectly.

Here's how it helps:

  1. Writing Less but Enough Code: Instead of writing a ton of code all at once, TDD makes you focus on just what you need for each step. It's like making a sandwich - you only add the ingredients you need, not everything in the kitchen.

  2. Preventing Common Mistakes: TDD stops you from making big mistakes like making your code too complicated or missing important things your app needs. It's like having a friend check your homework to make sure you didn't forget anything.

  3. Concise and Clean Code: TDD encourages you to write neat and tidy code. It's like tidying up your room - everything has its place and it's easy to find what you need.

  4. Saves Time and Money: By catching problems early and keeping your code organized, TDD actually saves time and money in the long run. It's like fixing a leaky faucet before it floods your house - it's easier and cheaper to deal with it early on.

So, TDD is like having a roadmap for building your app: it keeps you on track, helps you avoid wrong turns, and gets you to your destination faster and with fewer headaches.

It follows a simple cycle: Red, Green, and Refactor.

  1. Red: Write a Failing Test

    • You start by writing a test that defines the behavior you want to implement in your app. This test initially fails because you haven't written any code to satisfy it yet.

    • In Android development, you might use testing frameworks like JUnit, Mockito, or Espresso for unit tests, mocking dependencies, and UI tests respectively.

    • For instance, if you're building a calculator app, you might write a test to ensure that adding two numbers works correctly.

  2. Green: Write the Minimum Code to Pass the Test

    • Now, you write the minimal code required to make the failing test pass. You're not concerned about code quality or performance at this stage; you're just aiming to make the test pass.

    • In our calculator example, you'd implement the logic for adding two numbers together.

  3. Refactor: Improve Code Quality

    • With the test passing, you can now refactor your code. Refactoring involves restructuring your code to improve readability, maintainability, or performance without changing its external behavior.

    • In Android development, you might extract methods, rename variables for clarity, or optimize algorithms.

    • Ensure that your tests still pass after refactoring. This ensures that you haven't unintentionally introduced bugs while improving your code.

  4. Repeat the Cycle

    • After refactoring, you repeat the cycle by writing another failing test for the next feature or improvement you want to make.

    • This iterative process continues throughout the development lifecycle, with tests acting as a safety net, ensuring that changes don't break existing functionality.

Types of Tests in Android Development

  • Unit Tests

  • Integration Tests

  • UI Tests

Unit Tests

These test individual units or components of your app in isolation, such as classes or functions. You can use JUnit and Mockito for writing unit tests in Android. Unit testing follows the principles of the β€œArrange, Act, Assert” (AAA) pattern.

Certainly! As an experienced Android developer, let's dive into how you can use JUnit and Mockito to write unit tests in Android Kotlin.

  1. JUnit: JUnit is a popular testing framework for Java and Kotlin. It provides annotations and assertions to write and organize tests effectively.

  2. Mockito: Mockito is a mocking framework for Java and Kotlin that allows you to create mock objects in your tests. Mock objects simulate the behavior of real objects, enabling you to isolate the unit of code you want to test.

Here's a step-by-step guide to writing a unit test using JUnit and Mockito in Android Kotlin:

Suppose you have a class called LoginManager responsible for handling user authentication. It has a method login() that communicates with a backend service to verify user credentials.

class LoginManager(private val authService: AuthService) {
    fun login(username: String, password: String): Boolean {
        return authService.authenticate(username, password)
    }
}

Writing the Unit Test:

  1. Set Up Your Test Environment:

    • Ensure that JUnit and Mockito dependencies are added to your build.gradle file.
    dependencies {
        testImplementation 'junit:junit:4.13'
        testImplementation 'org.mockito:mockito-core:3.11.2'
    }
  1. Write the Unit Test:

    • Create a Kotlin test file (e.g., LoginManagerTest.kt) in the test directory.

    • Use JUnit annotations to define your test class and methods.

    import org.junit.Test
    import org.junit.Assert.*
    import org.mockito.Mockito.*

    class LoginManagerTest {

        @Test
        fun `login should return true when authentication succeeds`() {
            // Arrange
            val authService = mock(AuthService::class.java)
            `when`(authService.authenticate(anyString(), anyString())).thenReturn(true)

            val loginManager = LoginManager(authService)

            // Act
            val result = loginManager.login("username", "password")

            // Assert
            assertTrue(result)
        }

        @Test
        fun `login should return false when authentication fails`() {
            // Arrange
            val authService = mock(AuthService::class.java)
            `when`(authService.authenticate(anyString(), anyString())).thenReturn(false)

            val loginManager = LoginManager(authService)

            // Act
            val result = loginManager.login("username", "password")

            // Assert
            assertFalse(result)
        }
    }
  • In these tests:

    • Arrange: We create a mock AuthService object using Mockito and define its behavior using when() and thenReturn().

    • Act: We call the login() method of LoginManager with test credentials.

    • Assert: We verify if the method returns the expected result.

  1. Run the Tests:

    • Run the tests in Android Studio or using Gradle to ensure they pass.

By using JUnit and Mockito, you can effectively write unit tests for your Android Kotlin code. Mockito helps you isolate the unit under test by mocking dependencies, making your tests more focused and reliable. Unit tests ensure that individual components of your app behave as expected, leading to a more robust and stable application.

Integration Tests

Integration tests in Android Kotlin involve testing how different parts of your app work together. Unlike unit tests that focus on testing individual components in isolation, integration tests verify interactions between components, ensuring they integrate correctly and produce the desired behavior as a whole. For Android, you might use frameworks like Robolectric.

Robolectric

Robolectric is a testing framework for Android that allows you to run tests on the JVM (Java Virtual Machine) without needing to deploy them to a device or emulator. This makes testing faster and more convenient, especially for integration tests that require interactions with Android framework components.

Suppose we have a simple Android app with a CalculatorActivity that performs basic arithmetic operations. We want to write an integration test to verify that the UI elements and logic in this activity work correctly.

Writing the Integration Test:

  1. Set Up Your Test Environment:

    • Ensure that Robolectric dependency is added to your build.gradle file:
    gradleCopy codetestImplementation 'org.robolectric:robolectric:4.6.1'
  1. Write the Integration Test:

    • Create a Kotlin test file (e.g., CalculatorActivityTest.kt) in the test directory.

    • Use Robolectric's @RunWith annotation to specify the test runner.

    import android.widget.Button
    import android.widget.EditText
    import org.junit.Assert.assertEquals
    import org.junit.Before
    import org.junit.Test
    import org.junit.runner.RunWith
    import org.robolectric.Robolectric
    import org.robolectric.RobolectricTestRunner
    import org.robolectric.annotation.Config

    @RunWith(RobolectricTestRunner::class)
    @Config(sdk = [Config.OLDEST_SDK])
    class CalculatorActivityTest {

        private lateinit var activity: CalculatorActivity
        private lateinit var editTextNumber1: EditText
        private lateinit var editTextNumber2: EditText
        private lateinit var addButton: Button

        @Before
        fun setUp() {
            activity = Robolectric.buildActivity(CalculatorActivity::class.java).create().start().resume().get()
            editTextNumber1 = activity.findViewById(R.id.editTextNumber1)
            editTextNumber2 = activity.findViewById(R.id.editTextNumber2)
            addButton = activity.findViewById(R.id.addButton)
        }

        @Test
        fun testAddition() {
            // Enter numbers
            editTextNumber1.setText("5")
            editTextNumber2.setText("3")

            // Click the add button
            addButton.performClick()

            // Verify result
            assertEquals("8", activity.findViewById<TextView>(R.id.resultTextView).text)
        }
    }
  • In this test:

    • We use Robolectric's Robolectric.buildActivity() method to create an instance of CalculatorActivity.

    • In the setUp() method annotated with @Before, we initialize the activity and obtain references to its UI elements.

    • In the testAddition() method annotated with @Test, we simulate user interaction by setting text in the EditText fields and clicking the add button.

    • We then verify that the result displayed matches the expected sum.

  1. Run the Tests:

    • Run the tests in Android Studio or using Gradle to ensure they pass.
  2. Interpret the Results:

    • If the test passes, it indicates that the CalculatorActivity is functioning correctly, and the integration between UI elements and logic is successful.

    • If the test fails, investigate the failure reason, fix any issues, and rerun the test.

Robolectric allows you to test Android components in a simulated environment, providing fast and reliable integration tests without the need for a physical device or emulator. By leveraging Robolectric, you can ensure the correctness and robustness of your Android Kotlin applications.

UI Tests

These test the app's user interface, ensuring that UI elements and interactions behave as expected. UI tests, also known as user interface tests or end-to-end tests, verify that the user interface of your app behaves correctly across different screens and interactions. Espresso is a popular choice for UI testing in Android.

Suppose we have a simple Android app with a login feature. We want to write a UI test to verify that the login screen (LoginActivity) functions correctly.

Writing the UI Test:

  1. Set Up Your Test Environment:

    • Ensure that the appropriate testing dependencies are added to your build.gradle file, including Espresso and JUnit.
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
  1. Write the UI Test:

    • Create a Kotlin test file (e.g., LoginActivityUITest.kt) in the androidTest directory.

    • Use Espresso to interact with UI elements and verify their behavior.

    import androidx.test.espresso.Espresso.onView
    import androidx.test.espresso.action.ViewActions.*
    import androidx.test.espresso.assertion.ViewAssertions.*
    import androidx.test.espresso.matcher.ViewMatchers.*
    import androidx.test.ext.junit.rules.ActivityScenarioRule
    import org.junit.Rule
    import org.junit.Test

    class LoginActivityUITest {

        @get:Rule
        var activityScenarioRule = ActivityScenarioRule(LoginActivity::class.java)

        @Test
        fun testLoginSuccess() {
            // Type username and password
            onView(withId(R.id.editTextUsername)).perform(typeText("user"))
            onView(withId(R.id.editTextPassword)).perform(typeText("password"), closeSoftKeyboard())

            // Click login button
            onView(withId(R.id.buttonLogin)).perform(click())

            // Verify navigation to home screen
            onView(withId(R.id.home_activity_layout)).check(matches(isDisplayed()))
        }

        @Test
        fun testLoginFailure() {
            // Type invalid username and password
            onView(withId(R.id.editTextUsername)).perform(typeText("invalidUser"))
            onView(withId(R.id.editTextPassword)).perform(typeText("invalidPassword"), closeSoftKeyboard())

            // Click login button
            onView(withId(R.id.buttonLogin)).perform(click())

            // Verify error message is displayed
            onView(withText("Invalid credentials")).check(matches(isDisplayed()))
        }
    }
  • In this test:

    • We use Espresso's onView() method to locate UI elements by their IDs.

    • We use perform() to simulate user interactions, such as typing text into EditText fields and clicking buttons.

    • We use check() to verify the expected outcome, such as checking if the home screen is displayed after successful login or if an error message is displayed for failed login attempts.

  1. Run the Tests:

    • Run the tests in Android Studio by right-clicking on the test file and selecting "Run 'LoginActivityUITest'".

    • Alternatively, execute the tests using Gradle from the command line.

  2. Interpret the Results:

    • If the tests pass, it indicates that the login screen functions correctly, and the UI interactions behave as expected.

    • If any test fails, investigate the failure reason, fix any issues, and rerun the test.

By writing UI tests, you can ensure that the user interface of your app behaves correctly across different scenarios and interactions, providing a smooth and reliable user experience.

Common mistakes to avoid

  1. Testing Too Much or Too Little at Once:

    • Don't try to test everything in one go, but also don't ignore important cases.
  2. Not Testing Extreme Conditions:

    • Make sure you test the limits, like the smallest and largest values, to catch any unexpected problems.
  3. Tests Depending on Each Other:

    • Each test should be independent, so one test's success or failure doesn't affect others.
  4. Messy Test Data:

    • Keep your test data organized and don't hardcode it directly into tests.
  5. Forgetting to Test Error Handling:

    • Check if your app handles errors gracefully, not just when everything goes smoothly.
  6. Weak Assertions:

    • Double-check your test results to make sure they cover everything you want to test.
  7. Ignoring Performance Issues:

    • Test how your app performs under pressure, like when lots of people are using it at once.
  8. Forgetting to Update Tests:

    • Keep your tests up-to-date as you change your app, or they might not catch new bugs.
  9. Not Checking Enough Code:

    • Make sure your tests cover all the important parts of your app's code.
  10. Skipping Test Documentation:

    • Write down what each test is supposed to do, so anyone reading it knows what to expect.

It's time to wrap up the blog devs and also it's time to end our series. Thank you for following this series. I hope this series has been a helpful resource for simplifying the process of learning Android development.


Connect with Me:

Hey there! If you enjoyed reading this blog and found it informative, why not connect with me on LinkedIn? 😊 You can also follow my Instagram page for more mobile development-related content. πŸ“²πŸ‘¨β€πŸ’» Let’s stay connected, share knowledge and have some fun in the exciting world of app development! 🌟

Check out my Instagram page

Check out my LinkedIn

Β