Episode ini menguji aplikasi Echo: unit test handler dengan echo.New dan httptest, menguji middleware secara langsung, mocking service dengan antarmuka, serta benchmark testing.B untuk mengukur alokasi dan latensi route.

Salah satu keuntungan terbesar Echo adalah kemudahannya diuji. Karena berdiri di atas net/http, seluruh tooling pengujian Go bekerja tanpa adaptasi. Arsitektur berlapis dari episode 8 juga membuat setiap lapisan bisa diuji secara terpisah.
Episode 17 ini menguji aplikasi Echo: unit test handler dengan echo.New() dan httptest, menguji middleware, mocking service dengan antarmuka, serta benchmark dengan testing.B untuk mengukur alokasi dan latensi.
Handler Echo diuji dengan membuat instance Echo, mendaftarkan route, lalu mengirim request buatan lewat httptest:
func TestHello(t *testing.T) {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "hello")
})
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, ingin %d", rec.Code, http.StatusOK)
}
if rec.Body.String() != "hello" {
t.Fatalf("body = %q, ingin %q", rec.Body.String(), "hello")
}
}httptest.NewRequest membuat request buatan, httptest.NewRecorder menangkap respons. e.ServeHTTP(rec, req) menjalankan seluruh pipeline — router, middleware, handler — persis seperti request nyata.
Untuk handler yang menerima payload, lampirkan body dan header:
func TestCreateUser(t *testing.T) {
e := echo.New()
e.Validator = &CustomValidator{validator: validator.New()}
e.POST("/users", createUserHandler)
body := strings.NewReader(`{"name":"Arman","email":"a@dev.id"}`)
req := httptest.NewRequest(http.MethodPost, "/users", body)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("status = %d, ingin %d", rec.Code, http.StatusCreated)
}
}Perhatikan bahwa validator harus didaftarkan di instance test juga — instance test adalah instance lengkap, bukan irisan.
Middleware diuji langsung dengan context buatan:
func TestSecurityHeaders(t *testing.T) {
e := echo.New()
handler := securityHeaders()(func(c echo.Context) error {
return c.String(http.StatusOK, "ok")
})
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if err := handler(c); err != nil {
t.Fatalf("handler gagal: %v", err)
}
if rec.Header().Get("X-Frame-Options") != "DENY" {
t.Fatal("X-Frame-Options tidak terpasang")
}
}e.NewContext membuat context Echo dari request dan recorder buatan — cara langsung menguji satu middleware tanpa server berjalan.
Arsitektur episode 8 membuat mocking sederhana: service menerima antarmuka UserRepository, dan test cukup menyediakan implementasi tiruan:
type mockRepo struct{}
func (m *mockRepo) FindByID(ctx context.Context, id int) (*model.User, error) {
if id != 42 {
return nil, errors.New("user tidak ditemukan")
}
return &model.User{ID: 42, Name: "Arman"}, nil
}
func TestGetUser(t *testing.T) {
svc := service.NewUserService(&mockRepo{})
h := handler.NewUserHandler(svc)
e := echo.New()
h.RegisterRoutes(e.Group("/api/v1"))
req := httptest.NewRequest(http.MethodGet, "/api/v1/users/42", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, ingin %d", rec.Code, http.StatusOK)
}
}Handler diuji tanpa database sama sekali. &mockRepo{} menentukan perilaku data, sehingga logika handler bisa diverifikasi dalam isolasi.
Benchmark mengukur alokasi dan latensi route. Polanya hampir sama dengan test, hanya memakai testing.B:
func BenchmarkHello(b *testing.B) {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "hello")
})
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
e.ServeHTTP(rec, req)
}
}Jalankan:
go test -v ./...
go test -bench=. -benchmem ./internal/handler/go test -bench=. -benchmem menampilkan jumlah alokasi per operasi. Perhatikan dua angka: alokasi per op dan jumlah byte — keduanya target optimasi di episode 19.
Episode 17 menguji aplikasi Echo dari semua sudut: unit test handler dengan echo.New() dan httptest, pengujian middleware lewat e.NewContext, mocking service dengan antarmuka, serta benchmark testing.B untuk mengukur latensi dan alokasi.
Inti yang harus dibawa pulang:
httptest.NewRequest dan httptest.NewRecorder.e.NewContext memungkinkan menguji middleware secara langsung.testing.B dengan e.ServeHTTP berulang.-benchmem mengukur alokasi, target optimasi di episode 19.go test -race untuk mendeteksi data race.Di episode 18 selanjutnya kita akan membahas observability & monitoring — metrik Prometheus, tracing dengan OpenTelemetry, structured logs dengan slog, health check endpoint, serta integrasi dashboard dan alerting untuk latensi, error rate, dan throughput.