Skip to content

Instantly share code, notes, and snippets.

@ynwd
Last active November 26, 2020 06:29
Show Gist options
  • Save ynwd/6421c8cf6de55b8d05cf0cea78ce5f95 to your computer and use it in GitHub Desktop.
Save ynwd/6421c8cf6de55b8d05cf0cea78ce5f95 to your computer and use it in GitHub Desktop.
Super Easy Interface Usage Explanation

Kegunaan Interface

Catatan ini diadaptasi dari artikel berikut.
Konteks tulisan ini bisa dilihat di page sebelumnya

Di dunia node.js dan JavaScript, testing dan mocking bisa dilakukan dengan mocha, sinon, chai dan kawan-kawannya. Cara pemakaiannya simple, tapi agak tricky.

Nah, kalau di golang, salah satu yang populer adalah testify. Catatan ini sebenarnya merupakan pendahuluan agar bisa memahami penggunaannya.

Interface bisa dipakai untuk menjalankan berbagai method yang memiliki signature yg sama. Kesamaan itu berupa nama method, argumen, dan return valuenya.

Kemampuan inilah yang digunakan testify untuk melakukan mock.

Bagaimana ceritanya? Mari kita lanjut.


Misalkan kita ingin membuat modul yang gunanya menampilkan sambutan (Greet).

Konten greet tersebut berasal dari sebuah repo yang mendapatkan nilainya dari database (via FetchMessage).

Dalam perspektif struct, berarti kita akan membuat 2:

  1. repo: untuk akses database via method FetchMessage()
  2. greeter: untuk menjalankan method Greet(). Dari method inilah nanti FetchMessage milik repo dipanggil.

Diagramnya seperti ini:

Sedikit tambahan:

  • Pada kasus yang nyata, repo ini digunakan untuk melakukan operasi CRUD ke database. Di sinilah nantinya semua query ditulis.
  • Hanya saja, dalam catatan ini, repo hanya mengembalikan nilai tertentu. Tidak ada koneksi ke database.

Perhatikan kode berikut:

package main

import "fmt"

type repo struct{}

func (d *repo) FetchMessage(lang string) (string, error) {
	return "bzzzz", nil
}

type greeter struct {
	Repo repo
	lang string
}

func (g *greeter) Greet() string {
	msg, _ := g.Repo.FetchMessage(g.lang)
	return msg
}

func main() {
	r := repo{}
	g := greeter{r, "en"}
	msg := g.Greet()
	fmt.Println(msg)
}

Sebenarnya, bisa saja repo ini ditulis memakai struct biasa seperti di atas. Tapi karena keseluruhan service nanti akan dites menggunakan testify, bentuk repo ini harus berupa struct yang punya interface.

Interface itulah nanti yang akan digunakan testify untuk mocking atau menggantikan nilai dari database sesuai dengan keinginan kita.

Diagramnya jadi seperti ini:

Yang jadi pertanyaan:

  • Seperti apa bentuk interface untuk repo?

Repository interface

Lihat kode berikut:

type Repository interface {
	FetchMessage(lang string) (string, error)
}

Interface ini nanti digunakan sebagai type data di constructor. lihat source code berikut.

Juga perhatikan: method di interface hanya mendefinisikan nama dan signature. Sementara itu isinya masih kosong. method yang masih kosong itu, nanti di-implementasi di struct.

repo struct

Lihat kode berikut:

type repo struct{}

func (d *repo) FetchMessage(lang string) (string, error) {
	return "bzzzz", nil
}

Perhatikan: nama method dan signature-nya harus sama dengan interface. Juga perhatikan, di method sudah ada implementasinya.

Implementasi method inilah yang nanti-nya akan diambil alih controlnya oleh testify saat testing.

repo constructor

Lihat kode berikut:

func NewRepository() Repository {
	return new(repo)
}

Sebenarnya ini adalah fungsi biasa yang return value-nya ber-tipe Repository. Hanya saja, yang berasal dari TypeScript atau Java, bisa menganggap fungsi ini menggantikan kegunaan constructor.

Cara memakainya:

repo := NewRepository()

Untuk struct greeter, penjelasannya hampir-hampir mirip dengan struct repo.

Berikut ini code-codenya:

Greeter interface

type GreeterService interface {
	Greet() string
}

greeter struct

Coba perhatikan tipe data Repo di struct berikut.

type greeter struct {
	Repo Repository
	lang string
}

func (g greeter) Greet() string {
	msg, _ := g.Repo.FetchMessage(g.lang)
	return msg
}

Tipe datanya adalah interface Repository. Dan lihat, kita bisa langsung memanggil method FetchMessage() via Repo.

Yang jadi pertanyaan:

  • Di interface kan methodnya masih kosong. Bagaimana bisa FetchMessage() dijalankan?

Greeter constructor

Coba perhatikan kode berikut:

func NewGreeter(repo Repository, lang string) GreeterService {
	return greeter{repo, lang}
}

Cara pemakaian:

repo := NewRepository()
service := NewGreeter(repo, "en")

Yang menarik adalah:

  • Coba perhatikan variabel repo yang tipe datanya berupa interface Repository.
    Ia di-inject ke dalam contructor NewGreeter yang signature methodnya juga interface Repository. Dari sinilah struct greeter mendapatkan nilai aktual dari repository.
  • Poinnya pentingnya:
    interface bisa dimanfaatkan untuk tipe data. Method yang ada di dalamnya juga bisa langsung dipanggil via struct yang mengimplementasi.

Agar lebih jelas, kode utuh penjelasan di atas bisa dilihat di sini.

What's next

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment