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.
- Buat Project baru di Android Studio dengan kriteria sebagai berikut:
Nama Project MyUnitTesting Target & Minimum Target SDK Phone and Tablet, Api level 21 Tipe Activity Empty Activity Activity Name MainActivity Use AndroidX artifacts True Language Kotlin/Java - Lalu pada activity_main.xml tambahkan kode berikut:
<?xml version="1.0" encoding="utf-8"?>
Jangan lupa untuk menambahkan teks yang ada di layout ke dalam strings.xml.
<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><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> - 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);
}
} - 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();
}
} Nah setelah Anda menambahkan 2 kelas baru, selanjutnya adalah mengimplementasikan kelas-kelas tersebut pada MainActivity. Silahkan ubah kelas tersebut menjadi seperti berikut ini:
Selanjutnya tambahkan aksi ketika btnCalculate diklik dan beri response-nya ketika berhasil. Maka MainActivity menjadi seperti berikut: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);
}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);
}
}Nah, sampai di sini Anda sudah membuat aplikasi Cuboid dengan pattern MVVM. Silakan jalankan aplikasi Anda dan hasilnya akan jadi seperti ini:
Selanjutnya Anda akan melakukan Unit Testing pada project tersebut.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'
}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/ .
Kemudian akan muncul dialog Create Test, centang semua fungsi yang akan dibuat unit testing-nya.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 sajaSelanjutnya 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);
}Nah, untuk menjalankan pengujiannyanya, silakan tekan CTRL+SHIFT+F10 atau klik kanan pada class dan pilih Run 'MainViewModelTest',
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'.
dan lihat hasilnya: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.- Sekarang coba jalankan pengujiannya sekali lagi dan lihat hasilnya.
Akan ada indikator berwarna hijau. Pesan tests passed menandakan bahwa Unit Testing berhasil. - 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);
}
} Jalankan pengujian dan lihat hasilnya:
Berhasil! Anda sudah melakukan testing pada proses perhitungan luas permukaan dan keliling.
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);
}
}Jalankan dan lihat hasilnya:
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' |
Java |
testImplementation 'junit:junit:4.12' |
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:
- @RunWith(MockitoJUnitRunner.class)
Fungsinya untuk menginisialisai frameworkMockito. - @Mock
Fungsinya untuk membuat obyek mock yang akan menggantikan obyek yang asli. - @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. - @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.
- assertEquals
Fungsi ini merupakan fungsi dari JUnit yang digunakan untuk memvalidasi output yang diharapkan dan output yang sebenarnya. - Verify
Digunakan untuk memeriksa metode dipanggil dengan arguman yang diberikan. Verify merupkan fungsi dari framework Mockito - any
Merupakan fungsi dari Mockito yang digunakan untuk mencocokan argumen yang fleksibel. any digunakan bareng dengan verify. - when()
Digunakan untuk menandakan event di mana Anda ingin memanipulasi behavior dari mock object. - 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: