Unit Testing Menggunakan Library Go
Pemrograman tidak mudah bahkan programmer
terbaik tidak mampu menulis program yang bekerja persis seperti yang diinginkan setiap saat. Oleh karena itu bagian penting dari proses pengembangan perangkat lunak adalah pengujian (testing
). Menulis test
untuk kode kita adalah cara yang baik untuk memastikan kualitas dan meningkatkan keandalan.
Go menyediakan package testing, berisikan banyak sekali tools untuk keperluan unit test. Pada chapter ini kita akan belajar mengenai testing, benchmark, dan juga testing menggunakan testify.
Go menyertakan program khusus yang membuat tes menulis lebih mudah, jadi mari kita buat beberapa test
untuk paket yang kita buat di sesi ini. Di folder 01-math
buat file baru bernama math_test.go
yang berisi ini.
Sebelumnya kita telah buat suatu function sebagai berikut ini.
package main
func Average(xs []float64) float64 {
total := float64(0)
for _, x := range xs {
total += x
}
return total / float64(len(xs))
}
agar kita bisa membuat unit test maka kita generate menggunakan vscode
, maka akan membuat file baru math_test.go
dan men-generate fungsi TestAverage
dengan isi dibawah ini.
func TestAverageGenerate(t *testing.T) {
type args struct {
xs []float64
}
tests := []struct {
name string
args args
want float64
}{
{
name: "must 10",
args: args{
xs: []float64{10.0, 10.0},
},
want: float64(10),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Average(tt.args.xs); got != tt.want {
t.Errorf("Average() = %v, want %v", got, tt.want)
}
})
}
}
Unit Test Program Ganjil Genap
Buat fungsi untuk menentukan bahwa program tersebut mengeluarkan informasi yang diinput terkait angka ganjil
dan genap
.
func GanjilGenap(angka int) string {
if angka%2 == 0 {
return "genap"
}
return "ganjil"
}
Maka kita akan buat unit test-nya seperti ini:
package math
import (
"testing"
)
func TestAverageGenerate(t *testing.T) {
type args struct {
xs []float64
}
tests := []struct {
name string
args args
want float64
}{
{
name: "must 10",
args: args{
xs: []float64{10.0, 10.0},
},
want: float64(10),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Average(tt.args.xs); got != tt.want {
t.Errorf("Average() = %v, want %v", got, tt.want)
}
})
}
}
type testpair struct {
values []float64
average float64
}
var tests = []testpair{
{[]float64{1, 2}, 1.5},
{[]float64{1, 1, 1, 1, 1, 1}, 1},
{[]float64{-1, 1}, 0},
}
func TestAverage(t *testing.T) {
for _, pair := range tests {
v := Average(pair.values)
if v != pair.average {
}
}
}
func TestGanjilGenap(t *testing.T) {
type args struct {
angka int
}
tests := []struct {
name string
args args
want string
}{
{
name: "test case mengeluarkan ganjil",
args: args{
angka: 1,
},
want: "ganjil",
},
{
name: "test case mengeluarkan genap",
args: args{
angka: 2,
},
want: "genap",
},
{
name: "test case mengeluarkan -1",
args: args{
angka: -1,
},
want: "ganjil",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GanjilGenap(tt.args.angka); got != tt.want {
t.Errorf("GanjilGenap() = %v, want %v", got, tt.want)
}
})
}
}
Testing Tabung
Pertama siapkan terlebih dahulu sebuah struct Tabung
. Variabel object hasil struct ini nantinya kita gunakan sebagai bahan testing.
package math
import "math"
type Tabung struct {
Jarijari, Tinggi float64
}
func (t Tabung) Volume() float64 {
return math.Phi * math.Pow(t.Jarijari, 2) * t.Tinggi
}
func (t Tabung) Luas() float64 {
return 2 * math.Phi * t.Jarijari * (t.Jarijari + t.Tinggi)
}
func (t Tabung) KelilingAlas() float64 {
return 2 * math.Phi * t.Jarijari
}
Maka kita akan membuat unit test satu-satu dari fungsi yang sudah kita buat. Bisa dilihat sebagai berikut.
Unit Test untuk fungsi Volume
func TestTabung_Volume(t *testing.T) {
type fields struct {
Jarijari float64
Tinggi float64
}
tests := []struct {
name string
fields fields
want float64
}{
{
name: "testing hitung volume",
fields: fields{
Jarijari: 7, Tinggi: 10,
},
want: float64(792.8366544874485),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tr := Tabung{
Jarijari: tt.fields.Jarijari,
Tinggi: tt.fields.Tinggi,
}
if got := tr.Volume(); got != tt.want {
t.Errorf("Tabung.Volume() = %v, want %v", got, tt.want)
}
})
}
}
Unit Test untuk fungsi Luas Permukaan
func TestTabung_Luas(t *testing.T) {
type fields struct {
Jarijari float64
Tinggi float64
}
tests := []struct {
name string
fields fields
want float64
}{
{
name: "testing hitung luas permukaan",
fields: fields{
Jarijari: 7, Tinggi: 10,
},
want: float64(385.092089322475),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tr := Tabung{
Jarijari: tt.fields.Jarijari,
Tinggi: tt.fields.Tinggi,
}
if got := tr.Luas(); got != tt.want {
t.Errorf("Tabung.Luas() = %v, want %v", got, tt.want)
}
})
}
}
Unit test untuk Keliling Alas
func TestTabung_KelilingAlas(t *testing.T) {
type fields struct {
Jarijari float64
Tinggi float64
}
tests := []struct {
name string
fields fields
want float64
}{
{
name: "testing hitung keliling alas",
fields: fields{
Jarijari: 7, Tinggi: 10,
},
want: float64(22.65247584249853),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tr := Tabung{
Jarijari: tt.fields.Jarijari,
Tinggi: tt.fields.Tinggi,
}
if got := tr.KelilingAlas(); got != tt.want {
t.Errorf("Tabung.KelilingAlas() = %v, want %v", got, tt.want)
}
})
}
}
Cara eksekusi testing adalah menggunakan command go test
. Argument -v
atau verbose digunakan menampilkan semua output log pada saat pengujian.
Jalankan program seperti di bawah ini, terlihat bahwa tidak ada test yang fail.
➜ tabung git:(main) ✗ go test -v
=== RUN TestTabung_Volume
=== RUN TestTabung_Volume/testing_hitung_volume
--- PASS: TestTabung_Volume (0.00s)
--- PASS: TestTabung_Volume/testing_hitung_volume (0.00s)
=== RUN TestTabung_Luas
=== RUN TestTabung_Luas/testing_hitung_luas_permukaan
--- PASS: TestTabung_Luas (0.00s)
--- PASS: TestTabung_Luas/testing_hitung_luas_permukaan (0.00s)
=== RUN TestTabung_KelilingAlas
=== RUN TestTabung_KelilingAlas/testing_hitung_keliling_alas
--- PASS: TestTabung_KelilingAlas (0.00s)
--- PASS: TestTabung_KelilingAlas/testing_hitung_keliling_alas (0.00s)
PASS
ok github.com/santekno/tabung 0.486s
Method Test
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 |
Skiped() | Menampilkan laporan skip |
Parallel() | Menge-set bahwa eksekusi testing adalah parallel |
Catatan Perintah Test
Perintah untuk melihat coverage dari unit test dalam projek
➜ tabung git:(main) ✗ go test -coverprofile=coverage.out
PASS
coverage: 100.0% of statements
ok github.com/santekno/tabung 0.750s
Perintah untuk melihat code mana saja yang sudah cover unit test
➜ tabung git:(main) ✗ go tool cover -html=coverage.out
Maka hasilnya akan generate
html yang berupa visualiasi dari unit test yg sudah tercover ataupun yang belum
Perintah untuk melakukan generate ulang menggunakan moq
$ cd <folder-yang-akan-di-generate>
$ go generate ./...
Pastikan juga pada fungsi interface ditambahkan paling atas struct
seperti ini
// go:generate moq -out main_mock_test.go . UserRepositoryInterface
dengan aturan seperti ini go:generate moq -out <nama-file-mock-test> . <struct-interface-yang-akan-dibuat-mock>