Pendahuluan

Seringkali kita membutuhkan Line of Code yang banyak dari unit test kita agar code coverage kita menjadi

100%



Sehingga biasanya Code Unit Test kita ukurannya lebih besar daripada Code yang ditest itu sendiri.


Banyaknya Line of Code dari unit test kita ini biasanya karena ini :

  • Logic code nya misalnya cuma satu line saja, berisikan pengecekan kondisi If … then. Tetapi di dalam kalang If tersebut, terdapat pengecekan kondisi yang banyak. Sehingga kita mesti membuat banyak method di Code Unit Test untuk mengetes satu If .. tersebut.

  • Pengecekan Exception. Ketika kita membuat code yang menangkap banyak Exception di try…catch, maka code ini juga perlu di test.

  • Kombinasi dari semua kasus yang terjadi dalam satu method dari sebuah code.


Banyaknya test untuk code kita diatas, ditambah dengan waktu yang terbatas, mungkin menjadi alasan bagi Software Engineer untuk tidak membuat Unit Test untuk code yang dibuat.

Padahal sebenarnya Unit Test merupakan hal yang semestinya dilakukan ketika kita membuat sebuah code.

Sehingga ada istilah TDD (Test Driven Development) dalam membuat sebuah code.

Buat Test Unit/Test Code nya dulu, baru buat Code nya.

Ok..ok.., kita bukan membahas mengenai TDD kali ini.

Kita akan membahas mengenai Unit Test yang di-Parameterized


Kita coba bahas mengenai kasus diatas.


Apa masalahnya dengan banyaknya Line of Code dari unit test ?


Secara umum, banyaknya Line of Code dari unit test tidak bisa kita hindari.

Namanya juga mengetes semua kondisi, dan semua kemungkinan dari sebuah code.

Tentu saja harus dibikin Unit Test yang lengkap.

Semua nilai yang menjadi batas atas, batas bawah, nilai normal, null, kosong, dsb mesti dimasukkan dalam unit test kita.

Tetapi tentunya akan ada kekurangannya, misalnya :

  • Susah melihat Test Method yang mana saja perlu diubah kalau ada perubahan di code kita, karena banyaknya.

  • Perlu penamaan Test Method yang konsisten dan standar agar mencerminkan isi dari Test Method nya.


Contoh masalahnya gimana sih ?


Coba kita lihat code ini :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.huzefril.unittest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class ServiceA {

	public boolean method1(String input1) {

		if (StringUtils.isNotBlank(input1) && (input1.equals("A") || input1.equals("B"))) {
			log.info("Input is valid");
			return true;
		}

		return false;
	}
}

Code sederhana dengan hanya satu If..{ … } .

Kemudian kita coba untuk membuat Unit Testnya sbb :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.huzefril.unittest;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;

public class ServiceATest {

	@InjectMocks
	private ServiceA serviceA = new ServiceA();

	@Test
	public void testInput1_Null() {
		// call method
		boolean result = serviceA.method1(null);

		// assert
		assertFalse(result);
	}

	@Test
	public void testInput1_Empty() {
		// call method
		boolean result = serviceA.method1("");

		// assert
		assertFalse(result);
	}

	@Test
	public void testInput1_C() {
		// call method
		boolean result = serviceA.method1("C");

		// assert
		assertFalse(result);
	}

	@Test
	public void testInput1_D() {
		// call method
		boolean result = serviceA.method1("D");

		// assert
		assertFalse(result);
	}

	@Test
	public void testInput1_A() {
		// call method
		boolean result = serviceA.method1("A");

		// assert
		assertTrue(result);
	}

	@Test
	public void testInput1_B() {
		// call method
		boolean result = serviceA.method1("B");

		// assert
		assertTrue(result);
	}
}

Naah kaaan.. untuk satu method saja kita butuh 6 Test Method agar coverage nya bisa 100%


Solusinya ?

Kasus diatas bisa diselesaikan dengan Parameterized Test, sbb :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.huzefril.unittest;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;

public class ServiceATest {

	@InjectMocks
	private ServiceA serviceA = new ServiceA();

	@ParameterizedTest
	@NullAndEmptySource
	@ValueSource(strings = {"C","D"})
	public void testInput_False(String input) {
		// call method
		boolean result = serviceA.method1(input);

		// assert
		assertFalse(result);
	}

	@ParameterizedTest
	@ValueSource(strings = {"A","B"})
	public void testInput_True(String input) {
		// call method
		boolean result = serviceA.method1(input);

		// assert
		assertTrue(result);
	}

Nah kita lihat bahwa dari 6 Test Method di atas, kita bisa singkat cuma 2 Test Method.

Dan hasilnya sama antara code Unit Test yang pertama dengan yang kedua.

Dengan Parameterized Test seperti diatas, kita lihat , kita bisa menyingkat unit test kita hanya untuk 2 kasus saja.

  • Kasus kembalian nilainya false.
  • Kasus kembalian nilainya true.

Dengan Parameterized Test , maka :

  • Lebih mudah untuk mengkatogorisasikan jenis kasus di Unit test nya.
  • Lebih mudah mencari test method yang berkaitan.
  • Code nya lebih pendek dan lebih jelas.