42 Membuat Unit Test Query di graphql-go
Sebagai pengembang aplikasi backend modern, GraphQL bukanlah hal baru buat kita. Flexibilitas query, efisiensi data, dan self-documentation jadi alasan banyak tim beralih menggunakan GraphQL. Di ekosistem Go, library graphql-go menjadi pilihan populer. Namun, satu hal sering terlewatkan dalam penerapan GraphQL di Go: Unit Testing.
Unit test bukan sekedar formalitas. Ia jadi insurance policy terhadap bug di masa depan, terutama pada query yang jadi jembatan utama antara client dan server. Artikel ini akan membedah langkah demi langkah cara membuat unit test pada query di graphql-go, lengkap dengan contoh kode, simulasi mock data, dan best practice.
Mengapa Query di Unit Test?
Table : Manfaat Unit Test pada Query GraphQL
Manfaat | Penjelasan |
---|---|
Menjamin Correctness | Pastikan struktur response dan resolusi data benar |
Mendukung Refactoring | Menambah/mengubah resolver aman karena tester siap menangkap regression |
Dokumentasi Hidup | Konsumen backend tahu use case dan responsenya dengan membaca test case |
Testing Error Handling | Simulasi skenario error (contoh: unauthorized, data kosong) mudah dilakukan |
GraphQL-Go: Instalasi dan Skema Dasar
Sebelum masuk ke unit test, mari setup skema sederhana GraphQL di Go. Supaya fokus, kita buat query getUser(id: ID!): User
.
type User struct {
ID string
Name string
}
type Resolver struct{}
func (r *Resolver) GetUser(ctx context.Context, args struct{ ID string }) (*User, error) {
// Skenario: Ambil user by ID atau error jika tidak ditemukan
if args.ID == "42" {
return &User{ID: "42", Name: "Arthur Dent"}, nil
}
return nil, errors.New("user not found")
}
// schema.graphql
/*
type User {
id: ID!
name: String!
}
type Query {
getUser(id: ID!): User
}
*/
Integrasi ke server biasanya seperti ini:
schema := graphql.MustParseSchema(schemaString, &Resolver{})
http.Handle("/query", &relay.Handler{Schema: schema})
Dasar Unit Testing Query di graphql-go
Unit test pada graphql-go tidak mengakses endpoint HTTP. Ia langsung invoke resolver melalui schema, passing query dan variables. Keuntungannya, unit test bisa berjalan cepat dan isolated dari middleware/auth/network.
Dependensi
Tambahkan di go.mod
:
require (
github.com/graph-gophers/graphql-go v1.7.0
github.com/stretchr/testify v1.8.0
)
Membuat Unit Test Sederhana
Mari buat file test: resolver_test.go
.
Ingat, unit test Go wajib pakai function TestXxx(t *testing.T)
agar dikenali oleh go test.
package main
import (
"context"
"testing"
"github.com/graph-gophers/graphql-go"
"github.com/stretchr/testify/assert"
)
// Sederhanakan skema untuk test
const testSchema = `
type User {
id: ID!
name: String!
}
type Query {
getUser(id: ID!): User
}
`
// Implementasi resolver tiruan untuk test
type testResolver struct{}
func (r *testResolver) GetUser(ctx context.Context, args struct{ ID string }) (*User, error) {
if args.ID == "42" {
return &User{ID: "42", Name: "Tester"}, nil
}
return nil, nil
}
func TestGetUserQuery(t *testing.T) {
schema := graphql.MustParseSchema(testSchema, &testResolver{})
query := `
query ($id: ID!) {
getUser(id: $id) {
id
name
}
}
`
vars := map[string]interface{}{
"id": "42",
}
// Eksekusi query langsung ke skema (bukan HTTP)
response := schema.Exec(context.Background(), query, "GetUser", vars)
// status code selalu 200, cek bagian data & errornya
expected := `{"getUser":{"id":"42","name":"Tester"}}`
assert.JSONEq(t, expected, string(response.Data))
assert.Nil(t, response.Errors)
}
Simulasi Unit Test dengan Data Berbeda
Tes harus mencakup:
- Query data sukses
- Query data tidak ditemukan (should return nil)
- Query invalid argumen
Skema Simulasi
Berikut simulasi case branching yang diharapkan:
flowchart TD A[Start Test] --> B{Input ID?} B -- "42" --> C[Data Ada] C --> F[Sukses response] B -- "99" --> D[Data Tidak Ada] D --> G[Response Null] B -- "" --> E[Invalid Argumen] E --> H[Error Handling]
Mulai Tulis Table Test
Definisikan test table agar maintainable.
func TestGetUserQuery_TableDriven(t *testing.T) {
schema := graphql.MustParseSchema(testSchema, &testResolver{})
tests := []struct {
name string
inputID string
expData string
hasError bool
}{
{"ID ada", "42", `{"getUser":{"id":"42","name":"Tester"}}`, false},
{"ID tidak ada", "99", `{"getUser":null}`, false},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
query := `
query ($id: ID!) {
getUser(id: $id) {
id
name
}
}
`
vars := map[string]interface{}{
"id": tc.inputID,
}
resp := schema.Exec(context.Background(), query, "GetUser", vars)
assert.JSONEq(t, tc.expData, string(resp.Data))
})
}
}
Menguji Error & Edge Case
Bagaimana jika argumen tidak dikirim?
Eksekusi query dengan missing argumen akan mengembalikan error. Simulasikan juga di test.
func TestGetUserQuery_MissingArg(t *testing.T) {
schema := graphql.MustParseSchema(testSchema, &testResolver{})
query := `
query {
getUser { id name }
}
`
resp := schema.Exec(context.Background(), query, "", nil)
assert.NotNil(t, resp.Errors)
assert.Contains(t, resp.Errors[0].Message, "missing required argument")
}
Mocking Dependency
Resolver production biasanya ambil data via database/repository/service lain. Di unit test, hindari dependency pada resource eksternal!
Gunakan pattern dependency injection: resolver menerima interface service, dan di test, inject mock implementation.
type UserService interface {
FindByID(ctx context.Context, id string) (*User, error)
}
type ResolverWithDep struct {
Svc UserService
}
func (r *ResolverWithDep) GetUser(ctx context.Context, args struct{ ID string }) (*User, error) {
return r.Svc.FindByID(ctx, args.ID)
}
// Mock teknologi: bisa pakai gomock/mockery, atau cukup dummy struct
type mockUserSvc struct{}
func (m *mockUserSvc) FindByID(ctx context.Context, id string) (*User, error) {
if id == "42" {
return &User{ID: "42", Name: "Arthur"}, nil
}
return nil, nil
}
// Dalam test:
schema := graphql.MustParseSchema(testSchema, &ResolverWithDep{Svc: &mockUserSvc{}})
Unit testing dengan mock memastikan resolver hanya diuji pada logic, tidak tergantung eksternal service. Praktik ini penting dalam real world microservice.
Penutup: Checklist Unit Test Query GraphQL
Berikut checklist agar testing kita solid di project nyata:
- Query sukses
- Query data tidak ditemukan/null
- invalid/kurang argumen
- Simulasi error (misal dependency failure)
- Table test untuk maintainability
- Isolasi dependency via mock
- Cek data dan errors pada response
Kesimpulan
Membuat unit test untuk query di graphql-go
tidak sulit tapi sangat krusial. Testing langsung ke skema tanpa HTTP menghemat waktu, memudahkan refactor, dan meningkatkan confidence. Dengan table driven test, diagram flow, serta mock dependency, kita siap membangun backend GraphQL yang kokoh dan scalable.
Jangan pernah meluncurkan query GraphQL tanpa ter-cover oleh unit test.
Dengan coverage solid, kita bisa tidur nyenyak — tanpa takut bug diam-diam merusak produksi!
Referensi:
Sampai jumpa di workshop selanjutnya. 🚀
64. Implementasi gRPC Health Server
65. gRPC Name Resolver Kustom
Artikel Terhangat
43 Testing Mutation dan Validasi Input
08 Aug 2025
65. gRPC Name Resolver Kustom
08 Aug 2025
42 Membuat Unit Test Query di graphql-go
08 Aug 2025
64. Implementasi gRPC Health Server
08 Aug 2025
41 Pengenalan Unit Test untuk Resolver
08 Aug 2025
63. gRPC Health Checking Standar
08 Aug 2025

43 Testing Mutation dan Validasi Input

65. gRPC Name Resolver Kustom

42 Membuat Unit Test Query di graphql-go

64. Implementasi gRPC Health Server

41 Pengenalan Unit Test untuk Resolver
