App Testing: Unit Testing, Instrumented Testing, UI Testing with Espresso in Android Development


Testing is a crucial part of Android development to ensure that your app behaves as expected. Android provides different types of testing: unit testing, instrumented testing, and UI testing. In this article, we will explore each type of testing and provide examples using Kotlin.

1. Unit Testing in Android

Unit testing focuses on testing individual units of code, such as functions or methods, to ensure that they behave as expected. Unit tests are usually written using the JUnit framework.

1.1. Setting Up Unit Testing

To get started with unit testing in Android, you need to add the necessary dependencies in your build.gradle file:

    dependencies {
        testImplementation 'junit:junit:4.13.2'
        testImplementation 'org.mockito:mockito-core:3.11.2'
    }
        

JUnit is used for running the tests, and Mockito is used for mocking dependencies in tests.

1.2. Writing Unit Tests

Here's an example of a simple unit test using JUnit to test a function that adds two numbers:

    class Calculator {
        fun add(a: Int, b: Int): Int {
            return a + b
        }
    }

    class CalculatorTest {
        private lateinit var calculator: Calculator

        @Before
        fun setUp() {
            calculator = Calculator()
        }

        @Test
        fun testAdd() {
            val result = calculator.add(2, 3)
            assertEquals(5, result)  // Verifying the expected output
        }
    }
        

In this example, we create a Calculator class with an add method. The unit test ensures that the add method returns the correct result.

2. Instrumented Testing in Android

Instrumented tests are used to test the behavior of your app on an Android device or emulator. These tests run on the device itself and are used for components like activities and fragments that depend on Android APIs.

2.1. Setting Up Instrumented Testing

To set up instrumented testing, ensure that you have the following dependencies in your build.gradle file:

    dependencies {
        androidTestImplementation 'androidx.test.ext:junit:1.1.3'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    }
        

In this case, we are adding dependencies for JUnit (for running tests) and Espresso (for UI testing).

2.2. Writing Instrumented Tests

Instrumented tests are written in the androidTest directory. Here's an example of an instrumented test that checks if a button is displayed in an activity:

    @RunWith(AndroidJUnit4::class)
    class MainActivityTest {

        @Test
        fun testButtonIsDisplayed() {
            // Launching the activity
            val activityScenario = ActivityScenario.launch(MainActivity::class.java)

            // Checking if the button is displayed
            onView(withId(R.id.myButton)).check(matches(isDisplayed()))
        }
    }
        

This test launches the MainActivity and checks if a button with the ID myButton is displayed on the screen.

3. UI Testing with Espresso

Espresso is a powerful tool for testing the UI of your app. It allows you to simulate user interactions and verify that the UI behaves as expected.

3.1. Setting Up Espresso

Espresso is already included as part of the androidTestImplementation dependency in your build.gradle file, as mentioned earlier. However, you may need additional tools depending on your test case.

3.2. Writing UI Tests with Espresso

Espresso provides methods for interacting with views, checking view states, and performing user actions. Here's an example of a simple Espresso test that simulates a button click and verifies the text change on a TextView:

    @RunWith(AndroidJUnit4::class)
    class ButtonClickTest {

        @Test
        fun testButtonClickChangesText() {
            // Launching the activity
            val activityScenario = ActivityScenario.launch(MainActivity::class.java)

            // Performing a button click and verifying the TextView text
            onView(withId(R.id.myButton)).perform(click())
            onView(withId(R.id.myTextView)).check(matches(withText("Button Clicked!")))
        }
    }
        

In this test, we launch the MainActivity, perform a click action on a button with ID myButton, and check if the TextView with ID myTextView shows the expected text after the button click.

3.3. Espresso Matchers

Espresso provides several matchers to check various properties of views, such as:

  • isDisplayed(): Checks if the view is visible.
  • withText(): Checks if the view contains specific text.
  • isClickable(): Verifies if the view is clickable.
  • withId(): Matches a view by its ID.

Here’s an example of using isDisplayed() to check if a view is visible:

    onView(withId(R.id.myTextView)).check(matches(isDisplayed()))
        

3.4. Handling Async Tasks in UI Tests

In some cases, you may need to wait for asynchronous tasks (such as network requests or animations) to complete before continuing with your assertions. Espresso provides mechanisms like IdlingResources to synchronize your tests with background tasks.

For example, if you're testing a network request, you can use an IdlingResource to pause the test until the request completes:

    class NetworkIdlingResource : IdlingResource {
        // Implementation of the IdlingResource to wait for network response
    }
        

4. Best Practices for Testing

Here are some best practices to follow when writing tests for your Android app:

  • Write small, isolated tests: Test individual functions or components in isolation to keep tests simple and easy to maintain.
  • Mock external dependencies: Use mocking frameworks like Mockito to avoid calling real network services or databases in unit tests.
  • Use UI tests for critical user interactions: Write UI tests for important workflows to ensure the user interface behaves correctly.
  • Run tests frequently: Run your tests regularly to catch issues early in the development cycle.

5. Conclusion

Testing is a critical part of Android development, and Android provides robust frameworks for unit testing, instrumented testing, and UI testing. By writing and running tests with tools like JUnit, Espresso, and Android's testing libraries, you can ensure that your app functions as expected and provides a smooth user experience. Regular testing helps identify issues early, improving the overall quality of your app.





Advertisement