# Unit Testing

Pada Go kita dapat melakukan unit test menggunakan built-in [package](https://golang.org/pkg/testing/) yang sudah disediakan Go yaitu `testing` . Selain internal package, kita juga dapat menggunakan external package untuk proses unit testing pada Go. Salah satunya adalah [testify](https://github.com/stretchr/testify).

### Hello Testing

Kita akan mencoba membuat testing untuk mengecek apakah suatu fungsi mengembalikan sebuah string `Hello, Go Testing` . Pada file `main.go` kita akan membuat fungsi `sayHello` untuk mengembalikan string `Hello, Go Testing` dan memanggilnya pada fungsi `main` .

```go
package main

import "fmt"

func sayHello() string {
	return "Hello, Go Testing"
}

func main() {
	fmt.Println(sayHello())
}
```

Untuk melakukan testing, pada Go ada beberapa rules yang perlu kita perhatikan:

1. Kita harus membuat file baru dengan format nama file `xxxx_test.go` . Contoh `main_test.go`
2. Nama pada fungsi testing harus berawalan `Test` . Contoh `TestSayHello` .
3. Parameter pada fungsi testing hanya memiliki 1 parameter yaitu `t *testing.T`&#x20;
4. Untuk menggunakan `*testing.T` kita perlu melakukan import package `testing`&#x20;

Sekarang mari kita membuat file yang digunakan untuk melakukan testing. Kita akan membuat file bernama `main_test.go` . Kita masih membuat dalam package yang sama yaitu pada package `main` . Lalu kita melakukan import package `testing` .

Nama fungsi untuk testing yang kita buat adalah `TestSayHello` . Statement di dalam fungsi ini berisi beberapa hal. Kita membuat variabel baru `got` yang berperan untuk menyimpan hasil dari fungsi yang ingin kita testing.

```go
got := sayHello()
```

Lalu kita membuat 1 variabel baru `want` yang berperan untuk menyimpan suatu kondisi testing yang diharapkan.

```go
want := "Hello, Go Testing"
```

Jadi dari sini kita bisa melakukan sebuah kondisi menggunakan `if` untuk mengecek apakah fungsu `sayHello` yang telah kita buat sesuai ekpektasi hasilnya. Jika tidak maka akan menampilkan error.

```go
if got != want {
		t.Errorf("Got %s, but want %s", got, want)
	}
```

Kode lengkapnya dapat kamu lihat di bawah ini:

```go
package main

import "testing"

func TestSayHello(t *testing.T) {
	got := sayHello()
	want := "Hello, Go Testing"

	t.Logf("Text: %s", want)

	if got != want {
		t.Errorf("Got %s, but want %s", got, want)
	}
}
```

Untuk menjalankan testing pada Go, kita menggunakan perintah berikut ini pada command line:

```
go test
```

atau jika kita ingin menjalankan spesifik file tertentu dan ingin menampilkan output secara lengkap dapat menggunakan perintah berikut ini:

```
go test main.go main_test.go -v
```

Argument `-v` atau verbose digunakan menampilkan semua output log pada saat pengujian.

Jika kita jalankan program diatas maka hasilnya akan seperti berikut ini:

![menampilkan status PASS pada testing](/files/-MUhnAidoQVe6fjKtWk_)

Status testing diatas adalah `PASS` yang berarti lolos uji tes.

Kita akan membuat skenario jika hasil uji tes gagal yaitu dengan mengganti nilai dari variabel `want`&#x20;

```go
package main

import "testing"

func TestSayHello(t *testing.T) {
	got := sayHello()
	want := "Hello, world"

	t.Logf("Text: %s", want)

	if got != want {
		t.Errorf("Got %s, but want %s", got, want)
	}
}
```

Jika testing dilakukan kembali, maka akan terlihat seperti berikut ini:

![menampilkan status FAIL pada testing](/files/-MUhnsuIadbg_7hLyXln)

Maka hasilnya akan `FAIL` karena hasil uji test gagal memenuhi kriteria.

### Method Test

Kamu bisa melihat method lengkapnya yang dapat digunakan pada Go di tabel ini:

| Method       | Kegunaan                                                                     |
| ------------ | ---------------------------------------------------------------------------- |
| `Log()`      | Menampilkan log                                                              |
| `Logf()`     | Menampilkan log menggunakan format                                           |
| `Fail()`     | Menandakan terjadi `Fail()` dan proses testing fungsi tetap diteruskan       |
| `FailNow()`  | Menandakan terjadi `Fail()` dan proses testing fungsi dihentikan             |
| `Failed()`   | Menampilkan laporan fail                                                     |
| `Error()`    | `Log()` diikuti dengan `Fail()`                                              |
| `Errorf()`   | `Logf()` diikuti dengan `Fail()`                                             |
| `Fatal()`    | `Log()` diikuti dengan `failNow()`                                           |
| `Fatalf()`   | `Logf()` diikuti dengan `failNow()`                                          |
| `Skip()`     | `Log()` diikuti dengan `SkipNow()`                                           |
| `Skipf()`    | `Logf()` diikuti dengan `SkipNow()`                                          |
| `SkipNow()`  | Menghentikan proses testing fungsi, dilanjutkan ke testing fungsi setelahnya |
| `Skiped()`   | Menampilkan laporan skip                                                     |
| `Parallel()` | Menge-set bahwa eksekusi testing adalah parallel                             |

### Test Scenario

Kita akan membuat contoh lain dalam bentuk skenario. Anggaplah aplikasi Go kita ingin memiliki 2 fitur:

1. Aplikasi menampilkan teks `Hello, David` dimana `David` adalah nama dari seseorang yang akan kita kirim melalui argumen fungsi.
2. Jika saat memanggil fungsi tidak terdapat argumen nama seseorang, maka program harus menggantinya dengan teks `friend` .

Dari fitur di atas maka kita dapat membuat test case untuk 2 skenario tersebut.

Pertama, kita akan membuat test case terlebih dahulu pada file `main_test.go` :

```go
package main

import "testing"

func TestSayHello(t *testing.T) {
	t.Run("saying hello to people", func(t *testing.T) {
		got := sayHello("David")
		want := "Hello, David"

		t.Logf("Text: %s", want)

		if got != want {
			t.Errorf("Got %s, but want %s", got, want)
		}
	})

	t.Run("say 'Hello, Friend' when an empty string is supplied", func(t *testing.T) {
		got := sayHello("")
		want := "Hello, friend"

		t.Logf("Text: %s", want)

		if got != want {
			t.Errorf("Got %s, but want %s", got, want)
		}
	})
}
```

Kita bisa lihat dari kode di atas untuk melakukan Subtest, dapat menggunakan method `Run` .&#x20;

Lalu kita akan coba membuat program berdasarkan skenario tes di atas. Kita akan membuat program utama pada file `main.go` :

```go
package main

import "fmt"

func sayHello(name string) string {
	if name == "" {
		return "Hello, friend"
	}
	return "Hello, " + name
}

func main() {
	fmt.Println("Go Testing")
}
```

Jika kita jalankan testing pada kode di atas maka akan menampilkan hasil berikut ini:

![menampilkan hasil PASS pada testing](/files/-MUhyKAP_sJdNQItTwP0)

### Benchmark

Saat kita membuat program atau fungsi, tentunya kita ingin memastikan program yang kita buat sudah memiliki performa yang baik. Kita dapat melakukan testing performa fungsi pada program Go dengan cara benchmarking.

Untuk melakukan Benchmark, pada Go ada beberapa rules yang perlu yaitu nama pada fungsi benchmark testing harus berawalan `Benchmark` . Contoh `BenchmarkSayHello` .

Kita akan membuat fungsi sederhana pada file `main.go`&#x20;

```go
package main

import (
	"fmt"
)

func Calculate(x int) (result int) {
	result = x + 2
	return result
}

func main() {
	fmt.Println("Hello Benchmark Testing")
}
```

Kita akan mengecek performa fungsi `Calculate` menggunakan benchmark testing. Selanjutnya kita akan membuat testing pada file `main_test.go`&#x20;

```go
package main

import (
    "testing"
)

func BenchmarkCalculate(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Calculate(2)
    }
}
```

Untuk menjalankan benchmark testing agak sedikit berbeda yaitu dengan menjalankan perintah berikut ini pada command line

```
go test -bench=.
```

Maka hasil yang kita dapatkan&#x20;

![hasil benchmark testing pada fungsi Calculate](/files/-MUm7Vnv2dSBThdFZbTi)

Kita bisa melihat bahwa kita mengeksekusi fungsi `Calculate` sebanyak `1000,000,000` kali ,  pada kecepatan `0.3069` per operation, dan untuk keseluruhan pada kecepatan `1.462` detik untuk menjalankan fungsi tersebut.

Tentu saja nantinya makin kompleks sebuah fungsi maka akan mengalami peningkatan dari segi waktu. Dengan kita melakukan benchmark testing ini, kita akan lebih mengetahui bagaimana performa dari fungsi yang kita buat dan dapat meningkatkannya kembali.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://davidwinalda94.gitbook.io/mastering-golang/unit-testing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
