Belajar Menggunakan Unit Testing & Desing Pattern Aplikasi Android


Belajar Menggunakan Unit Testing & Desing Pattern Dalam Pengembangan Aplikasi Android agar semakin baik dan rapih coding dan anda juga dapat menghemat waktu dalam produksi nya.


Tujuan

Pada Codelab kali ini Anda akan menggunakan Junit untuk melakukan testing pada aplikasi Cuboid atau Balok. Poin penting yang tercakup dalam materi ini adalah bagaimana cara menggunakan Junit untuk mengetes unit testing Anda .


Logika Dasar

Menjalankan Aplikasi → melakukan pengujian secara manual → melakukan pengujian dengan Unit Testing → akan mendapatkan hasil pengujian secara otomatis.


Skenario Pengujian

Berikut ini adalah skenario pengujian Unit Testing yang akan dilakukan di codelab Unit Testing menggunakan Mockito:

  • Memastikan hasil volume balok sesuai dengan yang diharapkan.
  • Memastikan hasil luas permukaan balok sesuai dengan yang diharapkan.
  • Memastikan hasil keliling balok sesuai dengan yang diharapkan.
  • Memastikan metode hitung volume terpanggil.
  • Memastikan metode luas permukaan terpanggil.
  • Memastikan metode keliling terpanggil.


Codelab Unit Test Menggunakan Mockito

Pada latihan kali ini Anda akan membuat aplikasi Cuboid dengan menggunakan MVVM pattern.

  1. Buat Project baru di Android Studio dengan kriteria sebagai berikut:
    Nama ProjectMyUnitTesting
    Target & Minimum Target SDKPhone and Tablet, Api level 21
    Tipe ActivityEmpty Activity
    Activity NameMainActivity
    Use AndroidX artifactsTrue
    LanguageKotlin/Java

  2. Lalu pada activity_main.xml tambahkan kode berikut:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/length" />

    <EditText
    android:id="@+id/edt_length"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:inputType="numberDecimal"
    android:lines="1" />

    <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/width" />

    <EditText
    android:id="@+id/edt_width"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:inputType="numberDecimal"
    android:lines="1" />

    <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/height" />

    <EditText
    android:id="@+id/edt_height"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:inputType="numberDecimal"
    android:lines="1" />

    <Button
    android:id="@+id/btn_save"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/save" />

    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:orientation="horizontal">

    <Button
    android:id="@+id/btn_calculate_volume"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/calculate_volume"
    android:visibility="gone" />

    <Button
    android:id="@+id/btn_calculate_circumference"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/calculate_circumference"
    android:visibility="gone" />

    <Button
    android:id="@+id/btn_calculate_surface_area"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/calculate_surface_area"
    android:visibility="gone" />
    </LinearLayout>


    <TextView
    android:id="@+id/tv_result"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:gravity="center"
    android:text="@string/result"
    android:textSize="24sp"
    android:textStyle="bold" />


    </LinearLayout>
    Jangan lupa untuk menambahkan teks yang ada di layout ke dalam strings.xml.
    <resources>
    <string name="app_name">MyUnitTesting</string>
    <string name="width">Lebar</string>
    <string name="result">Hasil</string>
    <string name="calculate">Hitung</string>
    <string name="height">Tinggi</string>
    <string name="length">Panjang</string>

    </resources>

  3. Setelah itu buatlah kelas baru dan beri nama CuboidModel dan ketikkan kode berikut:
    Kotlin
    class CuboidModel {
    private var width: Double = 0.0
    private var length: Double = 0.0
    private var height: Double = 0.0

    fun getVolume(): Double = width * length * height

    fun getSurfaceArea(): Double {
    val wl = width * length
    val wh = width * height
    val lh = length * height

    return 2 * (wl + wh + lh)
    }

    fun getCircumference(): Double = 4 * (width + length + height)

    fun save(width: Double, length: Double, height: Double) {
    this.width = width
    this.length = length
    this.height = height
    }

    }
    Java
    class CuboidModel {
    private double width;
    private double length;
    private double height;

    public CuboidModel() {
    }

    public void save(double width, double length, double height) {
    this.width = width;
    this.length = length;
    this.height = height;
    }

    double getVolume() {
    return width * length * height;
    }

    public double getSurfaceArea() {
    double wl = width * length;
    double wh = width * height;
    double lh = length * height;

    return 2 * (wl + wh + lh);
    }

    public double getCircumference() {
    return 4 * (width + length + height);
    }

    }
  4. Lalu buatlah kelas baru dan beri nama MainViewModel dan ketikkan kode berikut:
    Kotlin
    class MainViewModel(private val cuboidModel: CuboidModel) {
    fun getCircumference(): Double = cuboidModel.getCircumference()

    fun getSurfaceArea(): Double = cuboidModel.getSurfaceArea()

    fun getVolume(): Double = cuboidModel.getVolume()

    fun save(l: Double, w: Double, h: Double) {
    cuboidModel.save(l, w, h)
    }

    }
    Java
    class MainViewModel {
    private final CuboidModel cuboidModel;

    MainViewModel(CuboidModel cuboidModel) {
    this.cuboidModel = cuboidModel;
    }

    void save(double l, double w, double h) {
    cuboidModel.save(l, w, h);
    }

    double getCircumference() {
    return cuboidModel.getCircumference();
    }

    double getSurfaceArea() {
    return cuboidModel.getSurfaceArea();
    }

    double getVolume() {
    return cuboidModel.getVolume();
    }

    }
  5. Nah setelah Anda menambahkan 2 kelas baru, selanjutnya adalah mengimplementasikan kelas-kelas tersebut pada MainActivity. Silahkan ubah kelas tersebut menjadi seperti berikut ini:

    Kotlin
    class MainActivity : AppCompatActivity() {

    private lateinit var mainViewModel: MainViewModel

    private lateinit var edtWidth: EditText
    private lateinit var edtHeight: EditText
    private lateinit var edtLength: EditText
    private lateinit var tvResult: TextView
    private lateinit var btnCalculateVolume: Button
    private lateinit var btnCalculateSurfaceArea: Button
    private lateinit var btnCalculateCircumference: Button
    private lateinit var btnSave: Button


    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    mainViewModel = MainViewModel(CuboidModel())

    edtWidth = findViewById(R.id.edt_width)
    edtHeight = findViewById(R.id.edt_height)
    edtLength = findViewById(R.id.edt_length)
    tvResult = findViewById(R.id.tv_result)
    btnCalculateVolume = findViewById(R.id.btn_calculate_volume)
    btnCalculateCircumference = findViewById(R.id.btn_calculate_circumference)
    btnCalculateSurfaceArea = findViewById(R.id.btn_calculate_surface_area)
    btnSave = findViewById(R.id.btn_save)

    }
    }
    Java
    public class MainActivity extends AppCompatActivity {

    private MainViewModel mainViewModel;

    private EditText edtWidth, edtHeight, edtLength;
    private TextView tvResult;
    private Button btnCalculateVolume, btnCalculateSurfaceArea, btnCalculateCircumference, btnSave;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mainViewModel = new MainViewModel(new CuboidModel());

    edtWidth = findViewById(R.id.edt_width);
    edtHeight = findViewById(R.id.edt_height);
    edtLength = findViewById(R.id.edt_length);
    tvResult = findViewById(R.id.tv_result);
    btnCalculateVolume = findViewById(R.id.btn_calculate_volume);
    btnCalculateCircumference = findViewById(R.id.btn_calculate_circumference);
    btnCalculateSurfaceArea = findViewById(R.id.btn_calculate_surface_area);
    btnSave = findViewById(R.id.btn_save);

    btnSave.setOnClickListener(this);
    btnCalculateSurfaceArea.setOnClickListener(this);
    btnCalculateCircumference.setOnClickListener(this);
    btnCalculateVolume.setOnClickListener(this);

    }
    Selanjutnya tambahkan aksi ketika btnCalculate diklik dan beri response-nya ketika berhasil. Maka MainActivity menjadi seperti berikut:
    Kotlin
    class MainActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var mainViewModel: MainViewModel

    private lateinit var edtWidth: EditText
    private lateinit var edtHeight: EditText
    private lateinit var edtLength: EditText
    private lateinit var tvResult: TextView
    private lateinit var btnCalculateVolume: Button
    private lateinit var btnCalculateSurfaceArea: Button
    private lateinit var btnCalculateCircumference: Button
    private lateinit var btnSave: Button

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    mainViewModel = MainViewModel(CuboidModel())

    edtWidth = findViewById(R.id.edt_width)
    edtHeight = findViewById(R.id.edt_height)
    edtLength = findViewById(R.id.edt_length)
    tvResult = findViewById(R.id.tv_result)
    btnCalculateVolume = findViewById(R.id.btn_calculate_volume)
    btnCalculateCircumference = findViewById(R.id.btn_calculate_circumference)
    btnCalculateSurfaceArea = findViewById(R.id.btn_calculate_surface_area)
    btnSave = findViewById(R.id.btn_save)

    btnSave.setOnClickListener(this)
    btnCalculateSurfaceArea.setOnClickListener(this)
    btnCalculateCircumference.setOnClickListener(this)
    btnCalculateVolume.setOnClickListener(this)

    }

    override fun onClick(v: View) {
    val length = edtLength.text.toString().trim()
    val width = edtWidth.text.toString().trim()
    val height = edtHeight.text.toString().trim()

    when {
    length.isEmpty() -> edtLength.error = "Field ini tidak boleh kosong"
    width.isEmpty() -> edtWidth.error = "Field ini tidak boleh kosong"
    height.isEmpty() -> edtHeight.error = "Field ini tidak boleh kosong"
    else -> {
    val l = length.toDouble()
    val w = width.toDouble()
    val h = height.toDouble()

    when {
    v.id == R.id.btn_save -> {
    mainViewModel.save(l, w, h)
    visible()
    }
    v.id == R.id.btn_calculate_circumference -> {
    tvResult.text = mainViewModel.getCircumference().toString()
    gone()
    }
    v.id == R.id.btn_calculate_surface_area -> {
    tvResult.text = mainViewModel.getSurfaceArea().toString()
    gone()
    }
    v.id == R.id.btn_calculate_volume -> {
    tvResult.text = mainViewModel.getVolume().toString()
    gone()
    }
    }
    }
    }
    }

    private fun visible() {
    btnCalculateVolume.visibility = View.VISIBLE
    btnCalculateCircumference.visibility = View.VISIBLE
    btnCalculateSurfaceArea.visibility = View.VISIBLE
    btnSave.visibility = View.GONE
    }

    private fun gone() {
    btnCalculateVolume.visibility = View.GONE
    btnCalculateCircumference.visibility = View.GONE
    btnCalculateSurfaceArea.visibility = View.GONE
    btnSave.visibility = View.VISIBLE
    }

    }
    Java
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private MainViewModel mainViewModel;

    private EditText edtWidth, edtHeight, edtLength;
    private TextView tvResult;
    private Button btnCalculateVolume, btnCalculateSurfaceArea, btnCalculateCircumference, btnSave;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mainViewModel = new MainViewModel(new CuboidModel());

    edtWidth = findViewById(R.id.edt_width);
    edtHeight = findViewById(R.id.edt_height);
    edtLength = findViewById(R.id.edt_length);
    tvResult = findViewById(R.id.tv_result);
    btnCalculateVolume = findViewById(R.id.btn_calculate_volume);
    btnCalculateCircumference = findViewById(R.id.btn_calculate_circumference);
    btnCalculateSurfaceArea = findViewById(R.id.btn_calculate_surface_area);
    btnSave = findViewById(R.id.btn_save);

    btnSave.setOnClickListener(this);
    btnCalculateSurfaceArea.setOnClickListener(this);
    btnCalculateCircumference.setOnClickListener(this);
    btnCalculateVolume.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
    String length = edtLength.getText().toString().trim();
    String width = edtWidth.getText().toString().trim();
    String height = edtHeight.getText().toString().trim();

    if (TextUtils.isEmpty(length)) {
    edtLength.setError("Field ini tidak boleh kosong");
    } else if (TextUtils.isEmpty(width)) {
    edtWidth.setError("Field ini tidak boleh kosong");
    } else if (TextUtils.isEmpty(height)) {
    edtHeight.setError("Field ini tidak boleh kosong");
    } else {
    double l = Double.parseDouble(length);
    double w = Double.parseDouble(width);
    double h = Double.parseDouble(height);

    if (v.getId() == R.id.btn_save) {
    mainViewModel.save(l, w, h);
    visible();
    } else if (v.getId() == R.id.btn_calculate_circumference) {
    tvResult.setText(String.valueOf(mainViewModel.getCircumference()));
    gone();
    } else if (v.getId() == R.id.btn_calculate_surface_area) {
    tvResult.setText(String.valueOf(mainViewModel.getSurfaceArea()));
    gone();
    } else if (v.getId() == R.id.btn_calculate_volume) {
    tvResult.setText(String.valueOf(mainViewModel.getVolume()));
    gone();
    }
    }
    }

    private void visible() {
    btnCalculateVolume.setVisibility(View.VISIBLE);
    btnCalculateCircumference.setVisibility(View.VISIBLE);
    btnCalculateSurfaceArea.setVisibility(View.VISIBLE);
    btnSave.setVisibility(View.GONE);
    }

    private void gone() {
    btnCalculateVolume.setVisibility(View.GONE);
    btnCalculateCircumference.setVisibility(View.GONE);
    btnCalculateSurfaceArea.setVisibility(View.GONE);
    btnSave.setVisibility(View.VISIBLE);
    }

    }
  6. Nah, sampai di sini Anda sudah membuat aplikasi Cuboid dengan pattern MVVM. Silakan jalankan aplikasi Anda dan hasilnya akan jadi seperti ini:
    20190726094605a56cf3a015350e64bc7491030ddd83af.gif

    Selanjutnya Anda akan melakukan Unit Testing pada project tersebut.

  7. Atur konfigurasi dependencies dengan menggunakan framework JUnit4 dan Mockito. Caranya edit build.gradle(module: app) dan tambahkan dependencies berikut:

    Kotlin
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
    testImplementation 'org.mockito:mockito-inline:3.0.0'
    Java
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.0.0'


    Sehingga kodenya seperti di bawah ini :

    Kotlin
    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android-extensions'
    apply plugin: 'kotlin-android'

    android {
    compileSdkVersion 29
    defaultConfig {
    applicationId "com.dicoding.picodiploma.myunittest"
    minSdkVersion 19
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    }
    }

    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation "androidx.core:core-ktx:1.0.2"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
    testImplementation 'org.mockito:mockito-inline:3.0.0'

    }
    Java
    apply plugin: 'com.android.application'

    android {
    compileSdkVersion 29
    defaultConfig {
    applicationId "com.dicoding.picodiploma.myunittest"
    minSdkVersion 19
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    }
    }

    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
    }
  8. Selanjutnya buka MainViewModel kemudian tekan CTRL+SHIFT+T atau ALT+ENTER pada teks MainViewModel dan pilih Create Test untuk membuat kelas MainViewModelTest pada package test/java/applicationIDKamu/ .
    2019090614462399f123a0382560b4d0ac698778c0c0c5Kemudian akan muncul dialog Create Test, centang semua fungsi yang akan dibuat unit testing-nya.
    2019090614492802f9a95dd1d473a63e9814718ec7cf9c

    Klik OK, akan muncul 2 pilihan yaitu androidTest untuk melakukan Instrumentation Test dan test untuk melakukan Unit Test.

    Pilih test karena Anda hanya akan melakukan Unit Test saja
    20190121142853eb979a1d8708c58ac837e7caae6bb26f
  9. Selanjutnya bukalah kelas MainViewModelTest. Pertama Anda akan menyiapkan kode-kode yang perlu disiapkan sebelum melakukan pengujian kelas viewmodel. Masukkan kode berikut ini:

    Kotlin
    class MainViewModelTest {

    private lateinit var mainViewModel: MainViewModel
    private lateinit var cuboidModel: CuboidModel


    @Before
    fun before() {
    cuboidModel = mock(CuboidModel::class.java)
    mainViewModel = MainViewModel(cuboidModel)
    }

    }
    Java
    public class MainViewModelTest {

    private MainViewModel mainViewModel;
    private CuboidModel cuboidModel;

    @Before
    public void before() {
    cuboidModel = mock(CuboidModel.class);
    mainViewModel = new MainViewModel(cuboidModel);
    }

    }

    Selanjutnya Anda akan menguji metode untuk menghitung volume dari sebuah balok, masukkanlah kode berikut:

    Kotlin
    class MainViewModelTest {

    private lateinit var mainViewModel: MainViewModel
    private lateinit var cuboidModel: CuboidModel

    private val dummyLength = 12.0
    private val dummyWidth = 7.0
    private val dummyHeight = 6.0

    private val dummyVolume = 500.0


    @Before
    fun before() {
    cuboidModel = mock(CuboidModel::class.java)
    mainViewModel = MainViewModel(cuboidModel)
    }

    @Test
    fun testVolume() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getVolume()
    assertEquals(dummyVolume, volume, 0.0001)
    }

    }
    Java
    public class MainViewModelTest {

    private MainViewModel mainViewModel;
    private CuboidModel cuboidModel;

    private final double dummyLength = 12.0;
    private final double dummyWidth = 7.0;
    private final double dummyHeight = 6.0;

    private final double dummyVolume = 500.0;


    @Before
    public void before() {
    cuboidModel = mock(CuboidModel.class);
    mainViewModel = new MainViewModel(cuboidModel);
    }

    @Test
    public void testVolume() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getVolume();
    assertEquals(dummyVolume, volume, 0.0001);
    }
  10. Nah, untuk menjalankan pengujiannyanya, silakan tekan CTRL+SHIFT+F10 atau klik kanan pada class dan pilih Run 'MainViewModelTest',
    20190906145916a896d639fea6041e427054fc130a3bc5dan lihat hasilnya:
    201907260958414baa2b8fe0b3b9c42f150878af6e5495

    Gagal! Unit testing gagal dan menunjukan indikator berwarna merah serta letak erornya. Di situ terlihat bahwa 'Expected = 500.0' dan 'Actual = 504.0'. Apa artinya? Pengujian Anda gagal, sebab ekspektasi mengenai hasil dari volume tersebut adalah '500.0' sementara hasil sebenarnya adalah '504.0'.

    Maka solusinya Anda ganti dengan ini:

    Kotlin
    private val dummyVolume = 504.0
    Java
    private double dummyVolume = 504.0;

    Nah, formula perhitungan volume sudah benar sekarang.

    Catatan:
    Angka 0.0001 pada parameter ketiga dalam assertEquals() adalah angka delta yang dimana merupakan selisih range di belakang koma bilangan double.

  11. Sekarang coba jalankan pengujiannya sekali lagi dan lihat hasilnya.
    20190726100344f69cf09b7222b545ce7aea1f4ba4f18a
    Akan ada indikator berwarna hijau. Pesan tests passed menandakan bahwa Unit Testing berhasil.

  12. Selanjutnya Anda akan coba menambahkan pengujian metode untuk menghitung luas permukaan dan keliling balok. Tambahkan 2 metode berikut:
    Kotlin
    class MainViewModelTest {

    private lateinit var mainViewModel: MainViewModel
    private lateinit var cuboidModel: CuboidModel

    private val dummyLength = 12.0
    private val dummyWidth = 7.0
    private val dummyHeight = 6.0

    private val dummyVolume = 504.0
    private val dummyCircumference = 100.0
    private val dummySurfaceArea = 396.0


    @Before
    fun before() {
    cuboidModel = mock(CuboidModel::class.java)
    mainViewModel = MainViewModel(cuboidModel)
    }

    @Test
    fun testVolume() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getVolume()
    assertEquals(dummyVolume, volume, 0.0001)
    }

    @Test
    fun testCircumference() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getCircumference()
    assertEquals(dummyCircumference, volume, 0.0001)
    }

    @Test
    fun tesSurfaceArea() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getSurfaceArea()
    assertEquals(dummySurfaceArea, volume, 0.0001)
    }

    }
    Java
    public class MainViewModelTest {

    private MainViewModel mainViewModel;
    private CuboidModel cuboidModel;
    private final double dummyLength = 12.0;
    private final double dummyWidth = 7.0;
    private final double dummyHeight = 6.0;
    private final double dummyVolume = 504.0;
    private final double dummyCircumference = 100.0;
    private final double dummySurfaceArea = 396.0;


    @Before
    public void before() {
    cuboidModel = mock(CuboidModel.class);
    mainViewModel = new MainViewModel(cuboidModel);
    }

    @Test
    public void testVolume() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getVolume();
    assertEquals(dummyVolume, volume, 0.0001);
    }

    @Test
    public void testCircumference() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getCircumference();
    assertEquals(dummyCircumference, volume, 0.0001);
    }

    @Test
    public void tesSurfaceArea() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getSurfaceArea();
    assertEquals(dummySurfaceArea, volume, 0.0001);
    }

    }
  13. Jalankan pengujian dan lihat hasilnya:
    20190726111943ef8271a64f6485eefb1cd11dbdd60a94

    Berhasil! Anda sudah melakukan testing pada proses perhitungan luas permukaan dan keliling.


  14. Selanjutnya Anda coba melakukan pengujian dengan menggunakan mock. Tambahkan metode berikut:

    Kotlin
    class MainViewModelTest {

    private lateinit var mainViewModel: MainViewModel
    private lateinit var cuboidModel: CuboidModel

    private val dummyLength = 12.0
    private val dummyWidth = 7.0
    private val dummyHeight = 6.0

    private val dummyVolume = 504.0
    private val dummyCircumference = 100.0
    private val dummySurfaceArea = 396.0

    @Before
    fun before() {
    cuboidModel = mock(CuboidModel::class.java)
    mainViewModel = MainViewModel(cuboidModel)
    }

    @Test
    fun testVolume() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getVolume()
    assertEquals(dummyVolume, volume, 0.0001)
    }

    @Test
    fun testCircumference() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getCircumference()
    assertEquals(dummyCircumference, volume, 0.0001)
    }

    @Test
    fun tesSurfaceArea() {
    cuboidModel = CuboidModel()
    mainViewModel = MainViewModel(cuboidModel)
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight)
    val volume = mainViewModel.getSurfaceArea()
    assertEquals(dummySurfaceArea, volume, 0.0001)
    }

    @Test
    fun testMockVolume() {
    `when`(mainViewModel.getVolume()).thenReturn(dummyVolume)
    val volume = mainViewModel.getVolume()
    verify(cuboidModel).getVolume()
    assertEquals(dummyVolume, volume, 0.0001)
    }

    @Test
    fun testMockCircumference() {
    `when`(mainViewModel.getCircumference()).thenReturn(dummyCircumference)
    val circumference = mainViewModel.getCircumference()
    verify(cuboidModel).getCircumference()
    assertEquals(dummyCircumference, circumference, 0.0001)
    }

    @Test
    fun testMockSurfaceArea() {
    `when`(mainViewModel.getSurfaceArea()).thenReturn(dummySurfaceArea)
    val surfaceArea = mainViewModel.getSurfaceArea()
    verify(cuboidModel).getSurfaceArea()
    assertEquals(dummySurfaceArea, surfaceArea, 0.0001)
    }

    }
    Java
    public class MainViewModelTest {

    private MainViewModel mainViewModel;
    private CuboidModel cuboidModel;
    private final double dummyLength = 12.0;
    private final double dummyWidth = 7.0;
    private final double dummyHeight = 6.0;
    private final double dummyVolume = 504.0;
    private final double dummyCircumference = 100.0;
    private final double dummySurfaceArea = 396.0;

    @Before
    public void before() {
    cuboidModel = mock(CuboidModel.class);
    mainViewModel = new MainViewModel(cuboidModel);
    }

    @Test
    public void testVolume() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getVolume();
    assertEquals(dummyVolume, volume, 0.0001);
    }

    @Test
    public void testCircumference() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getCircumference();
    assertEquals(dummyCircumference, volume, 0.0001);
    }

    @Test
    public void tesSurfaceArea() {
    cuboidModel = new CuboidModel();
    mainViewModel = new MainViewModel(cuboidModel);
    mainViewModel.save(dummyWidth, dummyLength, dummyHeight);
    double volume = mainViewModel.getSurfaceArea();
    assertEquals(dummySurfaceArea, volume, 0.0001);
    }

    @Test
    public void testMockVolume() {
    when(mainViewModel.getVolume()).thenReturn(dummyVolume);
    double volume = mainViewModel.getVolume();
    verify(cuboidModel).getVolume();
    assertEquals(dummyVolume, volume, 0.0001);
    }

    @Test
    public void testMockCircumference() {
    when(mainViewModel.getCircumference()).thenReturn(dummyCircumference);
    double volume = mainViewModel.getCircumference();
    verify(cuboidModel).getCircumference();
    assertEquals(dummyCircumference, volume, 0.0001);
    }

    @Test
    public void testMockSurfaceArea() {
    when(mainViewModel.getSurfaceArea()).thenReturn(dummySurfaceArea);
    double volume = mainViewModel.getSurfaceArea();
    verify(cuboidModel).getSurfaceArea();
    assertEquals(dummySurfaceArea, volume, 0.0001);
    }

    }
  15. Jalankan dan lihat hasilnya:
    20190726112901fe8a3a55b43855cebe7e198376b57449


Hore! pengujian kelas perhitungan balok sudah rampung semua! Mulai dari perhitungan volume, keliling, dan luas permukaan.

Selesai sudah testing yang dilakukan, mari kita ke bedah kode.

Bedah Kode

Anda sudah melewati beberapa latihan untuk melakukan unit testing. Pada Bedah Kode ini Anda akan mendapat penjelasan mendalam tentang kode-kode yang digunakan pada latihan unit testing.

OK mari kita pelajari satu per satu:


Unit Testing Dependencies

Kotlin
testImplementation 'junit:junit:4.12'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
testImplementation 'org.mockito:mockito-inline:3.0.0'
Java
testImplementation 'junit:junit:4.12'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.0.0'

Dependencies yang digunakan pada latihan tadi adalah JUnit dan Mockito. JUnit digunakan untuk melakukan unit testing sedangkan Mockito digunakan sebagai mock object.

Fungsi dari mock object adalah untuk mereplika obyek yang digunakan oleh obyek yang sedang Anda test. Tujuannya agar test yang dilakukan hanya dilakukan pada unit yang berada di dalam jangkauan obyek yang sedang di-test tanpa berpengaruh pada obyek di luar jangkauan.


Manfaat penggunaan Mocking

Beberapa manfaat dari mocking meliputi:

  • Menghindari Terlalu Banyak Dependency. Mocking mengurangi ketergantungan fungsi. Misalnya, jika Anda memiliki fungsi Kelas yang bergantung pada fungsi B, Anda perlu menulis beberapa unit test yang mencakup fitur yang diberikan oleh fungsi B. Misalkan kode itu berkembang di masa depan dan Anda memiliki lebih banyak fungsi, yaitu A tergantung pada B, B bergantung pada C, dan C bergantung pada D. Jika kesalahan dikenalkan pada Z, semua unit test Anda akan gagal.
  • Mengurangi kelebihan beban (overload). Ini berlaku untuk fungsi resource-intensive. Sebuah mock dari fungsi itu akan mengurangi penggunaan sumber daya (resource) yang tidak perlu selama pengujian, sehingga mengurangi waktu uji coba.
  • Bypass kendala waktu dalam fungsi. Ini berlaku untuk aktivitas terjadwal. Bayangkan sebuah proses yang telah dijadwalkan untuk dijalankan setiap jamnya. Dalam situasi seperti ini, mocking benar-benar menguji logika sehingga Anda tidak harus menjalankan test berjam-jam, menunggu sampai selesai.


Annotation

Di dalam Unit Testing tadi Anda telah menggunakan beberapa annotation, di antaranya:

  1. @RunWith(MockitoJUnitRunner.class)
    Fungsinya untuk menginisialisai frameworkMockito.
  2. @Mock
    Fungsinya untuk membuat obyek mock yang akan menggantikan obyek yang asli.
  3. @Before
    Fungsinya untuk menginisialisasi method sebelum melakukan test. Method yang diberi anotasi @Before ini akan dijalankan sebelum menjalankan semua method dengan anotasi @Test. Selain anotasi @Before, dalam melakukan Unit Testing juga ada anotasi @After yang berfungsi sebaliknya dari anotasi @Before, yaitu untuk menginisialisai method yang akan dijalankan setelah method dengan anotasi @Test.
  4. @Test
    Anotasi ini digunakan pada method yang akan dites.


Fungsi Test yang Digunakan

Dalam proses Unit Testing tadi Anda menggunakan 3 fungsi yaitu assertEquals, verify, dan any. Namun masih ada beberapa fungsi dari JUnit dan Mockito yang bisa digunakan. Berikut adalah penjelasan dari beberapa fungsi dari JUnit dan Mockito yang biasa digunakan dalam UnitTesting.

  1. assertEquals
    Fungsi ini merupakan fungsi dari JUnit yang digunakan untuk memvalidasi output yang diharapkan dan output yang sebenarnya.
  2. Verify
    Digunakan untuk memeriksa metode dipanggil dengan arguman yang diberikan. Verify merupkan fungsi dari framework Mockito
  3. any
    Merupakan fungsi dari Mockito yang digunakan untuk mencocokan argumen yang fleksibel. any digunakan bareng dengan verify.
  4. when()
    Digunakan untuk menandakan event di mana Anda ingin memanipulasi behavior dari mock object.
  5. thenReturn()
    Digunakan untuk memanipulasi output dari mock object.


Untuk memperdalam tentang design pattern dan unit testing silakan cek halaman ini.

Untuk source code materi, silakan unduh di tautan berikut ini:

*

Post a Comment (0)
Previous Post Next Post