# Android: Testing with Robolectric

> If get to know something new by reading my articles, don't forget to endorse me on [LinkedIn](https://www.linkedin.com/in/rommansabbir/)

# What is Unit Testing?

***Unit Testing*** is a type of software testing where individual units or components of software are tested. The purpose is to **validate** that each unit of the software code performs as **expected**. ***Unit Testing*** is done during the development (coding phase) of an application by the **developers**. ***Unit Tests*** isolate a section of code and verify its correctness. A unit may be an **individual function**, **method**, **procedure**, **module**, or **object**.

[More about Unit Testing](https://www.guru99.com/unit-testing-guide.html)

# What is Roboletric?

Running tests on an Android emulator or device is slow! Building, deploying, and launching the app often takes a minute or more. That’s no way to do **TDD**. There must be a better way.

Robolectric is the industry-standard unit testing framework for Android. [Robolectric](http://robolectric.org/) is a framework that brings fast and reliable unit tests to **Android**. **Tests** run inside the **JVM** on your workstation in seconds.

Robolectric provides a **JVM-compliant** version of the `android.jar` file. **Robolectric** handles inflation of views, resource loading, and lots of other stuff that’s implemented in native C code on Android devices.

This enables you to run your Android tests in your continuous integration environment without any additional setup. *Roboletric* supports resource handling, e.g., inflation of `views`. You can also use the `findViewById()` to search in a `view`.

Roboletric is not an integration test framework, i.e., you cannot test the interaction of Android components with it.

Roboletric does not require additional mocking frameworks, of course, it is still possible to use frameworks like Mockito if desired.

# Benefits of Roboletric?

* Run tests in a simulated Android environment inside a JVM
    
* Reduced overhead and flakiness of an emulator
    
* Run 10x faster than those on cold-started emulators
    
* Supports 17 different versions of Android
    

# Let's simplify Roboletric

* A testing tool for Android
    
* Can test pure Java/Kotlin code
    
* Provide the ability to test Android Framework code
    
* Don't require a simulator to run the test
    
* It uses the host machine to run the tests and which is much faster
    

# Installation

Update your app `build.gradle`

```kotlin
android {
    android {
        testOptions {
            unitTests {
                includeAndroidResources = true
            }
        }
    }
}

dependencies {
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.robolectric:robolectric:4.9'
}
```

**Sync** the project and **Roboletric** is ready for use

# First Test

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1683276859364/c61b8b91-e3a1-44b0-bb3b-0e9e8f583f1d.png align="center")

Let's create a class called `RoboletricFirstTest` under the `test` package.

```kotlin
@RunWith(RobolectricTestRunner::class)
class RoboletricFirstTest {
    private var conditionToMatch = ""

    @Before
    fun setup() {
        conditionToMatch = "Hello Roboletric!"
    }

    @Test
    fun `our first test to match the greetings`() {
        assert(conditionToMatch == "Hello Roboletric!")
    }

    @After
    fun release() {
        conditionToMatch = ""
    }
}
```

Run the **test** `(ctrl+shift+F10)`and check the output:

```kotlin
✅ Tests passed: 1 out 1 test
```

**Congratulations**, we successfully passed our first **Roboletric Test**.

**Things to notice**:

* `testImplementation 'org.robolectric:robolectric:4.9'` test implementation is applicable for `test` package only but not for `androidTest` package. For `androidTest` package use `androidTestImplementation`.
    
* Test classes must be annotated with `@RunWith(RobolectricTestRunner::class)`
    
* In a class, there might be several tests and to run those tests, we may need to initialize some dependent objects before the test runs. To initialize objects use the annotation `@Before` in your method.
    
* And to release the object when the tests are finished, use the annotation `@After` in your method.
    

# Practical Testing with Repository

Let's think about practical **testing** where we might want to test our **repository**, repository is responsible for making a **network call** and returning the **response**. \[*But for testing purposes, we are using a mock repository implementation*\]

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1683281340170/b75eeb9a-af13-4d4e-9933-e9d07f6cae08.png align="center")

Create a new repository(`interface`) under a package called `com.rommansabbir.robolectricexample.basictesting`

```kotlin
interface AuthRepository {
    fun login(username : String, password : String): String
    fun register(username: String, password: String): Boolean
}
```

Our auth repository contains two APIs called `login` and `register`. In a real project, we will have the implementation of network calls.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1683281545742/02f52952-26a4-446e-8567-c6c7e94126c3.png align="center")

Create two new classes called `AuthRepositoryTestImpl` and `AuthRepositoryTest` under the package called `com. rommansabbir.roboletricexample.basictesting`(`test` package).

```kotlin
class AuthRepositoryTestImpl : AuthRepository {
    override fun login(username: String, password: String): String {
        //make network request here
        return if (username == "rommansabbir" && password == "123456") "login_access" else "Login error"
    }

    override fun register(username: String, password: String): Boolean {
        //make network request here
        return username == "rommansabbir" && password == "123456"
    }
}
```

Here is our test implementation of auth repository.

```kotlin
@RunWith(RobolectricTestRunner::class)
class AuthRepositoryTest {
    private var authRepo: AuthRepository? = null

    @Before
    fun setup() {
        authRepo = AuthRepositoryTestImpl()
    }

    @Test
    fun `test a valid login`() {
        assert(authRepo?.login("rommansabbir", "123456") == "login_access")
    }

    @Test
    fun `test a invalid login`() {
        assert(authRepo?.login("rommansabbir", "password") == "Login error")
    }

    @Test
    fun `test a valid registration`() {
        assert(authRepo?.register("rommansabbir", "123456") == true)
    }

    @Test
    fun `test a invalid registration`() {
        assert(authRepo?.register("rommansabbir", "password") == false)
    }

    @After
    fun release() {
        authRepo = null
    }
}
```

Here are our tests for auth repository.

Run the **test** `(ctrl+shift+F10)`and check the output:

```kotlin
✅ Tests passed: 4 out 4 test
```

# Application Context-Related Test

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1683282964938/a1223789-5cf0-4241-8dbf-4a491f7b706c.png align="center")

In our test, we might need to use the **application context** to run tests (ex: `SharedPreferences` usages). Let's create a new class called `AppContextTest`.

```kotlin
@RunWith(RobolectricTestRunner::class)
class AppContextTest {
    private var context: Application? = null
    private var sharedPreferences: SharedPreferences? = null
    private val key: String = "TEST_KEY"

    @Before
    fun setup() {
        context = ApplicationProvider.getApplicationContext()
        sharedPreferences = context?.getSharedPreferences("test", Context.MODE_PRIVATE)
    }

    @Test
    fun `context and shared pref should be not null`() {
        assert(context != null)
        assert(sharedPreferences != null)
    }

    @Test
    fun `test a shared pref writing & get the written object`() {
        var writeDone = false
        sharedPreferences?.edit {
            putBoolean(key, true)
            commit()
            writeDone = true
        }
        assert(writeDone)
        assert(sharedPreferences?.getBoolean(key, false) == writeDone)
    }

    @After
    fun release() {
        sharedPreferences = null
        context = null
    }
}
```

Here, we are getting the application context by accessing the `ApplicationProvider.getApplicationContext()` API. We need to add the required dependency `testImplementation 'androidx.test:core:1.5.0'`

Run the **test** `(ctrl+shift+F10)`and check the output:

```kotlin
✅ Tests passed: 2 out 2 test
```

# Activity Testing

Let's test a `Activity`, where we will test the `button` name maches to a `string` (`resource`) and button `click` is performed.

```kotlin
@RunWith(RobolectricTestRunner::class)
class MainActivityTest {
    private var mainActivity: MainActivity? = null

    @Before
    fun setup() {
        mainActivity = Robolectric.buildActivity(MainActivity::class.java).create().resume().get()
    }

    @Test
    fun `check activity is initialized properly`() {
        assert(mainActivity != null)
    }

    @Test
    fun `check the button name`() {
        assert(mainActivity?.binding?.mainButton?.text == mainActivity?.getString(R.string.welcome_button))
    }

    @Test
    fun `check button pressed`() {
        var isPressed = false
        mainActivity?.binding?.mainButton?.apply {
            setOnClickListener{
                isPressed = true
            }
            performClick()
        }
        assert(isPressed)
    }

    @Test
    fun release() {
        mainActivity = null
    }
}
```

Run the **test** `(ctrl+shift+F10)`and check the output:

```kotlin
✅ Tests passed: 4 out 4 test
```

# Service Testing

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1683285748394/da4083f1-1134-46bd-9a4f-b01b23c1aec6.png align="center")

Create a new service called `MyService` under package `com.rommansabbir.robolectricexample.myservice`

```kotlin
class MyService : Service() {
    companion object {
        var isServiceInitialized = false
        var isServiceDestroyed = false
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        isServiceInitialized = true
    }

    override fun onDestroy() {
        super.onDestroy()
        isServiceDestroyed = true
    }
}
```

Create a class called `MyServiceTest` under package `com.rommansabbir.robolectricexample.myservice` (`test` package)

```kotlin
@RunWith(RobolectricTestRunner::class)
class MyServiceTest {
    private var service: MyService? = null

    @Before
    fun setup() {
        service = Robolectric.buildService(MyService::class.java).get()
        MyService.isServiceInitialized = false
        MyService.isServiceDestroyed = false
    }

    @Test
    fun `check if the service is created by calling onCreate method`() {
        service?.onCreate()
        assert(MyService.isServiceInitialized)
    }

    @Test
    fun `check if the service is destroyed by calling onDestroy method`() {
        service?.onDestroy()
        assert(MyService.isServiceDestroyed)
    }

    @After
    fun release() {
        service = null
    }
}
```

Run the **test** `(ctrl+shift+F10)`and check the output:

```kotlin
✅ Tests passed: 2 out 2 test
```

> If get to know something new by reading my articles, don't forget to endorse me on [LinkedIn](https://www.linkedin.com/in/rommansabbir/)

I hope this article will help you to cover basic **Android** testing with **Roboletric**.

**Project Link (Github)**: [Click here to view the repository](https://github.com/rommansabbir/RobolectricExample)

That's it for today. Happy Coding...
