CategoriesAndroidAutomated testAutomated testsProgramming

Testing API di Android dengan MockWebServer

Dalam membuat Aplikasi kita banyak bergantung ke REST API untuk mendapatkan data maupun mengirimkan data. tentunya kita memerlukan akses internet yang lancar agar proses development berjalan tanpa hambatan, itu karena kita langsung mengakses server REST API menggunakan koneksi internet kita. sebenarnya tidak ada salahnya melakukan testing dengan langsung call API dari server namun hal ini membuat kita bergantung ke kelancaran koneksi internet untuk mendapatkan data. hal ini dapat dicegah dengan membuat server tiruan / mock dengan data yang sama dengan server asli.

MockWebServer adalah library yang bisa membuat server tiruan, kita hanya perlu menyediakan contoh json dari server asli. selain response sukses kita juga bisa mensimulasikan response error. kita juga tidak memerlukan koneksi internet lagi karena hasil response sudah disimulasikan berdasarkan data asli. sekarang kita coba mempraktekkan.

Untuk mempermudah praktek download terlebih dahulu template project yang sudah saya buat, download / clone git berikut ini :

https://github.com/asyarialmuslimin/demo-api-testing

buka dengan Android Studio, jika tidak ada error maka akan menampilkan tampilan list seperti berikut ini :

jika sudah berhasil termuat sekarang edit file build.gradle (app). tambahkan library-library berikut ini untuk keperluan testing

//Mock web server
def MOCK_WEB_VERSION = "4.9.3"

androidTestImplementation "com.squareup.okhttp3:mockwebserver:$MOCK_WEB_VERSION"

androidTestImplementation "com.squareup.okhttp3:okhttp-tls:$MOCK_WEB_VERSION"

def ESPRESSO_VERSION = "3.4.0"

implementation "androidx.test.espresso:espresso-idling-resource:$ESPRESSO_VERSION"

androidTestImplementation "androidx.test.espresso:espresso-contrib:$ESPRESSO_VERSION"

androidTestImplementation "androidx.test.espresso:espresso-core:$ESPRESSO_VERSION"

androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$COROUTINES_VERSION"

implementation 'androidx.test:core-ktx:1.4.0'

setelah terinstall, tambahkan folder raw di project dengan klik kanan pada folder app > lalu pilih new > Folder > lalu klik Assets Folder. lalu klik Finish

setelah itu pada folder Assets buat file baru dengan nama “success_response.json”. isi file dengan text berikut :

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  },
  {
    "userId": 1,
    "id": 4,
    "title": "eum et est occaecati",
    "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit"
  },
  {
    "userId": 1,
    "id": 5,
    "title": "nesciunt quas odio",
    "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque"
  },
  {
    "userId": 1,
    "id": 6,
    "title": "dolorem eum magni eos aperiam quia",
    "body": "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae"
  },
  {
    "userId": 1,
    "id": 7,
    "title": "magnam facilis autem",
    "body": "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas"
  },
  {
    "userId": 1,
    "id": 8,
    "title": "dolorem dolore est ipsam",
    "body": "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae"
  },
  {
    "userId": 1,
    "id": 9,
    "title": "nesciunt iure omnis dolorem tempora et accusantium",
    "body": "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas"
  },
  {
    "userId": 1,
    "id": 10,
    "title": "optio molestias id quia eum",
    "body": "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error"
  }
]

file diatas merupakan simulasi response sukses dari API yang akan kita panggil di aplikasi

setelah itu buat satu class utility untuk memproses file json tersebut, beri nama file tersebut JsonConverter dan taruh di package util

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import java.io.IOException
import java.io.InputStreamReader

object JsonConverter {
    fun readStringFromFile(fileName: String): String {
        try {
            val applicationContext = ApplicationProvider.getApplicationContext<Context>()
            val inputStream = applicationContext.assets.open(fileName)
            val builder = StringBuilder()
            val reader = InputStreamReader(inputStream, "UTF-8")
            reader.readLines().forEach {
                builder.append(it)
            }
            return builder.toString()
        } catch (e: IOException) {
            throw e
        }
    }
}

setelah itu buat test untuk MainActivity, letakkan cursor di nama class MainActivity, lalu klik Alt + Enter dan pilih Create Test

pastikan menggunakan TestingLibrary JUnit4. lalu klik OK

pilih androidTest untuk folder testnya. lalu klik OK

setelah itu edit kode pada MainActivityTest, tambahkan konfigurasi untuk MockWebServer nya dan ActivityScenario untuk menentukan pada Activity mana test akan berjalan

@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    private val mockWebServer = MockWebServer()

    @Before
    fun setUp() {
        mockWebServer.start(8080)
        Const.BASE_URL = "http://127.0.0.1:8080"
        ActivityScenario.launch(MainActivity::class.java)
    }

    @After
    fun tearDown() {
        mockWebServer.shutdown()
    }
}

untuk port yang dipakai sesuaikan dengan port yang tidak dipakai di Komputer / Laptop anda agar tidak terjadi error, disitu kita juga merubah base url pada aplikasi menjadi base url local dengan port yang sudah kita sesuaikan. jadi nanti aplikasi tidak akan me request dari API server tapi akan merequest ke url local yang telah kita tentukan. jangan lupa men shutdown server mock pada function teardown agar server mock otomatis mati saat test sudah selesai.

kita disini memerlukan EspressoIdlingResource untuk mem pause test yang berjalan sampai request berhasil dijalankan. buat satu Object baru di package util dengan nama EspressoIdlingResource. isi file dengan kode sebagai berikut :

import androidx.annotation.VisibleForTesting
import androidx.test.espresso.idling.CountingIdlingResource

object EspressoIdlingResource {

    private const val RESOURCE = "GLOBAL"

    @VisibleForTesting
    @JvmField
    val countingIdlingResource = CountingIdlingResource(RESOURCE)

    fun increment() {
        countingIdlingResource.increment()
    }

    fun decrement() {
        if (!countingIdlingResource.isIdleNow) {
            countingIdlingResource.decrement()
        }
    }
}

setelah itu tambahkan kode EspressoIdlingResource.increment() untuk mem pause testing di MainActivity, taruh di atas kode untuk getStory. tambahkan juga EspressoIdlingResource.decrement() di dalam kode sukses untuk melanjutkan testing kembali. sehingga kode nya menjadi sebagai berikut :

tambahkan juga konfigurasinya pada MainActivityTest di function setUp() dan tearDown(), kode untuk konfigurasinya adalah sebagai berikut :

@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    private val mockWebServer = MockWebServer()

    @Before
    fun setUp() {
        mockWebServer.start(8080)
        Const.BASE_URL = "http://127.0.0.1:8080"
        ActivityScenario.launch(MainActivity::class.java)
        IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
    }

    @After
    fun tearDown() {
        mockWebServer.shutdown()
IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
    }
}

tambahkan juga konfigurasi pada AndroidManifest, tambahkan usesClearTextTraffic dengan value true karena kita menggunakan protocol http untuk mocking server.

lalu pada MainActivityTest buat satu function baru untuk mengetest jika response berhasil.

@Test
fun getData_Success() {
    // mocking response
    val mockResponse = MockResponse()
        .setResponseCode(200)
        .setBody(JsonConverter.readStringFromFile("success_response.json"))
    mockWebServer.enqueue(mockResponse)

    // mengecek apakah recyclerview terlihat
    onView(withId(R.id.rv_story)).check(matches(isDisplayed()))
    // scroll recyclerview dan mengecek apakah ada text yang cocok
    onView(withId(R.id.rv_story)).perform(
        RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
            hasDescendant(withText("magnam facilis autem"))
        )
    )
}

pada kode diatas pertama yang kita lakukan adalah melakukan mapping response nya. yaitu dengan menentukan ResponseCode dan body dari response nya. untuk ResponseCode kita atur ke 200 dan body nya kita ambil dari file success_response.json, setelah itu kita enqueue ke mockWebServer.

setelah itu baru kita lakukan pengecekan, jika berhasil maka otomatis yang tampil adalah rv_story, dan rv_story harus memiliki isi dan bisa di scroll dengan salah satu item adalah text “magnam facilis autem”, text tersebut ada di file success_response.json, jika kalian ingin mengganti text tersebut pastikan text nya ada di file success_response.json.

setelah itu coba run testnya dengan klik tombol play di sebelah kiri nama functionnya. jika berhasil akan jadi seperti berikut :

coba run testnya lagi tapi kali ini matikan koneksi internet pada device android nya. maka test nya akan tetap berhasil. ini dikarenakan MockWebServer berjalan di local jadi tidak memerlukan koneksi internet.

Sekarang kita coba mensimulasikan jika gagal mendapatkan data dari API. cukup mengganti responseCode ke 500 tanpa body. kode nya jadi seperti berikut :

@Test
fun getData_Failed() {
    // mocking response
    val mockResponse = MockResponse()
        .setResponseCode(500)
    mockWebServer.enqueue(mockResponse)
    // mengecek apakah layout error terlihat
    onView(withId(R.id.error_layout)).check(matches(isDisplayed()))
}

coba run, jika berhasil akan muncul centang hijau

Cukup mudah bukan testing dengan MockWebServer ? kita tidak perlu login dulu ke aplikasinya jika ingin test sebuah Activity, cukup di simulasikan response nya tidak perlu token authentikasi untuk Get data nya. tidak perlu get data berulang ulang dari server dan tidak menghabiskan kuota data juga karena kita get data dari server lokal 🙂

Referensi :
https://github.com/square/okhttp/tree/master/mockwebserver

Published by Ahmad Saifur Ridlo

Android Developer at Algostudio.net

Leave a Reply

Your email address will not be published. Required fields are marked *