title: “81. Unit Test untuk Handler gRPC: Panduan Lengkap bagi Engineer” description: Pelajari strategi, contoh kode, serta best practice membuat unit test berkualitas untuk handler gRPC di layanan backend modern. date: 2024-06-12 author: Seno Adi Prasetyo tags:
- grpc
- unit-testing
- backend
- software-engineering
- go
gRPC kini menjadi tulang punggung banyak aplikasi backend modern. Tak heran, performanya yang tinggi dan sistem type-safe membuat gRPC digemari untuk membangun microservice dan layanan antar server. Tetapi, stabilitas layanan tak hanya ditentukan oleh performa—melainkan juga oleh kualitas pengujian kode kita. Salah satu pengujian vital adalah unit test, terutama pada handler gRPC.
Pada artikel ini, saya akan membahas bagaimana menerapkan unit test yang efektif untuk handler gRPC, dilengkapi dengan studi kasus menggunakan bahasa Go. Saya juga akan memberikan tabel perbandingan strategi testing, simulasi, hingga diagram alur menggunakan mermaid yang bisa Anda adaptasi ke bahasa lain.
Mengapa Perlu Unit Test untuk Handler gRPC?
Handler gRPC adalah entry point seluruh logic permintaan klien. Di situlah validasi, business rule, hingga interaksi dengan dependensi eksternal digabungkan. Tanpa unit test, setiap bug kecil berpotensi menimbulkan downtime dan mimpi buruk bagi seluruh tim.
Berikut beberapa alasan utama mengapa unit test untuk handler gRPC penting:
| Alasan | Penjelasan |
|---|---|
| Validasi Input | Memastikan request yang masuk telah tervalidasi dengan benar |
| Simulasi logika bisnis | Menguji business logic sebelum benar-benar terhubung ke database/service lain |
| Regression Proof | Menghindari bug lama yang muncul lagi setelah update kode |
| Dokumentasi implicit | Test case bagus = cara lain membaca alur bisnis handler |
| Confidence untuk refactor | Risiko perubahan kode diperkecil |
Anatomy Handler gRPC Sederhana
Mari kita lihat contoh sederhana handler gRPC menggunakan Go.
// protos/user.proto
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse) {}
}
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
string id = 1;
string name = 2;
string email = 3;
}
Handler-nya kira-kira seperti ini:
// internal/handler/user.go
type UserRepository interface {
FindByID(ctx context.Context, id string) (*User, error)
}
type UserServiceHandler struct {
repo UserRepository
}
func (h *UserServiceHandler) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
user, err := h.repo.FindByID(ctx, req.Id)
if err != nil {
return nil, status.Errorf(codes.NotFound, "user not found")
}
return &pb.GetUserResponse{
Id: user.ID,
Name: user.Name,
Email: user.Email,
}, nil
}
Catatan: Contoh di atas sudah dipisahkan dependensi (UserRepository) agar easier to test.
Strategi Unit Testing Handler gRPC
Ketika menulis unit test handler gRPC, dependencies sebaiknya di-mock. Jangan benar-benar query ke database atau eksternal service.
Diagram Alur Testing Handler
flowchart TD
A[Setup Test] --> B[Mock Dependensi]
B --> C[Panggil Handler dengan Request]
C --> D[Verifikasi Response & Error]
D --> E[Bersihkan dan Selesai]
Paket Testing yang Bisa Dipakai
| Bahasa | Paket Test | Paket Mock |
|---|---|---|
| Go | testing | github.com/stretchr/testify/mock, gomock |
| Java | JUnit | Mockito |
| Python | unittest | unittest.mock |
| Node | jest | sinon |
Untuk Go, kita akan gunakan testify agar kode tetap bersih dan mudah dibaca.
Contoh Unit Test Handler gRPC (Go)
Membuat Mock Repository
// internal/repository/user_mock.go
import "github.com/stretchr/testify/mock"
type MockUserRepository struct {
mock.Mock
}
func (m *MockUserRepository) FindByID(ctx context.Context, id string) (*User, error) {
args := m.Called(ctx, id)
if user, ok := args.Get(0).(*User); ok {
return user, args.Error(1)
}
return nil, args.Error(1)
}
Menulis Unit Test untuk Handler
// internal/handler/user_test.go
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"yourproject/internal/repository"
pb "yourproject/protos"
)
func TestGetUser_Success(t *testing.T) {
// Setup
mockRepo := new(repository.MockUserRepository)
handler := &UserServiceHandler{repo: mockRepo}
testUser := &User{ID: "1", Name: "Budi", Email: "budi@email.com"}
// Arrange
mockRepo.On("FindByID", mock.Anything, "1").Return(testUser, nil)
// Act
req := &pb.GetUserRequest{Id: "1"}
resp, err := handler.GetUser(context.Background(), req)
// Assert
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, "1", resp.Id)
assert.Equal(t, "Budi", resp.Name)
assert.Equal(t, "budi@email.com", resp.Email)
mockRepo.AssertExpectations(t)
}
func TestGetUser_NotFound(t *testing.T) {
mockRepo := new(repository.MockUserRepository)
handler := &UserServiceHandler{repo: mockRepo}
mockRepo.On("FindByID", mock.Anything, "2").Return(nil, errors.New("not found"))
req := &pb.GetUserRequest{Id: "2"}
resp, err := handler.GetUser(context.Background(), req)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "user not found")
mockRepo.AssertExpectations(t)
}
Simulasi Eksekusi Test
Dengan kode di atas, test akan berjalan seperti berikut:
=== RUN TestGetUser_Success
--- PASS: TestGetUser_Success (0.00s)
=== RUN TestGetUser_NotFound
--- PASS: TestGetUser_NotFound (0.00s)
PASS
ok yourproject/internal/handler 0.005s
Best Practice Unit Testing Handler gRPC
Berikut beberapa best practice yang penting diterapkan:
- Isolasi Dependensi
- Gunakan interface & mock pattern agar handler mudah diuji terpisah dari DB/service eksternal.
- Uji Semua Skenario
- Cover success, not found, invalid input, dan error branch lain.
- Minimalkan Side Effect
- Handler sebaiknya pure: ubah state eksternal sekecil mungkin dalam scope test.
- Eksplisit Expectation
- Periksa return error, message, dan panggilan mock dengan detail.
- Automasi di CI
- Pastikan seluruh unit test dijalankan otomatis dalam pipeline CI/CD.
Kapan Gunakan Integration Test atau E2E?
Unit test hanya menguji isolasi logic. Jika ingin menguji flow utuh (handler, network, database), gunakan integration test/E2E.
Tetapi, unit test handler tetap must have karena cepat dieksekusi dan harga temuan bug-nya murah.
Kesimpulan
Unit test pada handler gRPC bukan hanya soal menaikkan coverage, tapi juga soal keandalan layanan. Dengan strategi dependency isolation, mock, dan cakupan skenario luas, engineering team akan memiliki peace of mind mengembangkan fitur baru tanpa takut bug lawas bangkit tiba-tiba.
Semoga dengan contoh, simulasi, dan diagram di atas, Anda makin mantap menulis unit test gRPC handler di proyek Anda berikutnya 🚀
Referensi
Happy testing!