Understanding Unit Test Android Kotlin
By: Salman Aziz
Tags: Kotlin, Testing, Android, Unit Testing, Tdd

We all know about testing that is done by any QA but after completing a feature or application but what if Application or feature has a bug in it ?

As per our routine we go to our bugs logs refactor code manually test it and send back to QA to test it back. This process is manual and take too much time.

Photo by Ghiffary Ridhwan on Unsplash

“So we have a solution for this problem which is to test your application during the development process”

What to do ?

You need to write test code to test your application code yes it feels weird to hear this but in actual this is the simple best thing we can do to make our code flawless.

Most of the developer think that writing Unit Test is very difficult even my thoughts were same before i enlighten myself with the Unit Tests

So dear it’s not difficult or any alien’s phenomena so you have to stop avoiding them.

Photo by Jo Leonhardt on Unsplash

Let dive in ocean of Unit Testing

This read seems lengthy but stay with me

Basically we have 2 types of test in Android application

1- Functional / Unit tests

2- Instrumentation tests

Unit Test

What is unit test ? Unit Test are use to test the small portion of you application code just any function in your view model or in you repository.

Don’t worry its very easy test it you just have to write test and run it and it will run on your local JVM you don’t need any physical mobile or emulator

Whenever you create any new android application, studio make test packages and class for you automatically

Before moving further you need to keep in mind your code will be have more test coverage if you follow TDD and S.O.L.I.D principles.

By default, Unit tests are written in src/test/java/ folder

You will have to add gradle dependencies to work with Unit testing

add this test dependency in you app.gradle file

testImplementation "junit:junit:4.13.2"

you are all set to perform your local unit test

Annotations

The JUnit framework uses annotation extensively. Some of the important annotations are as follows

@BeforeClass: Run only once before running all test cases

@AfterClass: Run only once after running all test cases

@Before: Run before executing each test case

@After: Runs after executing each test case

@Test: Tells that this is test case that need to verify

@Rule: Used to add behavior of test cases

Assertion

Assertion is used to pass or fail the test case below are some assertions

fail() − To explicitly make a test case fail.

− To explicitly make a test case fail. assertTrue(boolean test_condition) − Checks that the test_condition is true

− Checks that the test_condition is true assertFalse(boolean test_condition) − Checks that the test_condition is false

− Checks that the test_condition is false assertEquals(expected, actual) − Checks that both values are equal

− Checks that both values are equal assertNull(object) − Checks that the object is null

− Checks that the object is null assertNotNull(object) − Checks that the object is not null

− Checks that the object is not null assertSame(expected, actual) − Checks that both refers same object.

− Checks that both refers same object. assertNotSame(expected, actual) − Checks that both refers different object.

Above are the default assertions provided by JUnit you can also use any other dependency to for assertion Google Truth

testImplementation "com.google.truth:truth:1.0.1"

Now you have enough knowledge to continue and perform Unit Testing 🏋🏻

Lets start testing of real Application

We have Android application which search for repositories on GitHub and display it in Recyclerview and upon clicking any item it will open repository url in chrome mobile browser.

Network call will be performed by Retrofit and Coroutines will be used for Asynchronous implementation.

Setup project

Add below test dependencies in your app.gradle file

testImplementation "junit:junit:4.13.2"

testImplementation "org.hamcrest:hamcrest-all:1.3"

testImplementation "androidx.arch.core:core-testing:2.1.0"

testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.1"

testImplementation "com.google.truth:truth:1.1.3"

testImplementation "org.mockito:mockito-core:3.10.0"

testImplementation 'com.squareup.okhttp3:mockwebserver:4.7.2'

testImplementation 'org.mockito:mockito-inline:2.13.0'

testImplementation is require for local test that will be in src/test/java/ folder

For Android related testing you need to add androidTestImplementation but for now stick to only local Unit tests.

Below is RepoApi.kt class on which we need to perform testing

In order to test network call Unit we need mock data and mock server which dependency is already added above

Now create RepoApiTest.kt class in same package path but in test folder

From here we will start using our Annotations and our process will be as follow

1- create class with RepoApiTest.kt name in same package but in test folder

2- To test service call response we need to setup mockserver and response to verify that we need to return from network call.

3- Annotate a method with @Before and initialize service class and start mock server

@Before

fun createService() {

mockWebServer = MockWebServer()

service = Retrofit.Builder()

.baseUrl(mockWebServer.url("/"))

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(LiveDataCallAdapterFactory())



.build()

.create(RepoApi::class.java)

}

4- Place a response json file to do this you need to create a directory name “resources” in root test folder and place you success.json in this directory

5- Create a method and annotate it with @After to clean up instances and shutdown mock server

@After

fun stopService() {

mockWebServer.shutdown()

}

6- Finally create a method annotate it with @Test and write your test case

@Test

fun searchSuccess() {



enqueueResponse(

"success_json.json"

)

runBlocking {

val response = service.getSearches("foo",page = "1")



assertThat(response.isSuccessful).isTrue()

assertThat(response.body() ).isNotNull()

assertThat(response.body()?.totalCount).isEqualTo(2128713)

assertThat(response.body()?.items?.size).isEqualTo(2)



}

}

enqueueResponse is method that will add our response file as mock response

private fun enqueueResponse(fileName: String, headers: Map<String, String> = emptyMap()) {



val source=TestMockResponseFileReader(fileName)

val mockResponse = MockResponse()

for ((key, value) in headers) {

mockResponse.addHeader(key, value)

}

mockWebServer.enqueue(

mockResponse

.setBody(source.content )

)

}

searchSuccess test method verify 4 things

- returned response is successful

- response body is not null

- totalCount is 2128713

- current items in response are 2

if all these test pass we will get verification

lets say we are not getting any item then this test case will be fail

7- run and Refactor

Photo by Joshua Earle on Unsplash

You can get complete project here

I know this read is lengthy but you have to make efforts to accomplish something. Do let me know in case of any confusion.