Skip to main content

ประยุกต์ใช้กับ Fiber

Unit Test กับ Fiber

เมื่อเราพูดถึง Web Application ที่เราทำกันบน Go หนึ่งใน library ที่เราจะใช้คือ Fiber สำหรับทำ Web Server ดังนั้น เราจะมาลองทำ Unit test บน Fiber กันว่าทำยังไงได้บ้าง

code Service Fiber

เริ่มต้นเราจะลองสร้าง Service บน Fiber ก่อน โดย

  • ทำการลง Library Fiber
  • เพิ่ม API POST /users ที่จะทำการรับ 3 ค่าคือ email, fullname, age เข้ามา
  • ในแต่ละ field จะต้องทำการ validate ว่า
    • เป็น email ถูก format หรือไม่
    • เป็น fullname ถูก format หรือไม่ (ต้องไม่มีตัวเลขอยู่ในชื่อ)
    • age เป็นตัวเลขจริงๆหรือไม่ และมีค่ามากกว่า 1 หรือไม่

code ของ Fiber ก็จะมีหน้าตาประมาณนี้ โดยขั้นแรก ทำการลง library 2 ตัวนี้เพิ่มเข้ามา สำหรับ Fiber และ Validator

go get -u github.com/gofiber/fiber/v2
go get github.com/go-playground/validator/v10

ส่วน code ของ main.go ก็จะมีหน้าตาประมาณนี้

main.go
package main

import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"regexp"
)

var validate = validator.New()

// User struct with validation tags.
type User struct {
Email string `json:"email" validate:"required,email"`
Fullname string `json:"fullname" validate:"required,fullname"`
Age int `json:"age" validate:"required,numeric,min=1"`
}

// setup function initializes the Fiber app.
func setup() *fiber.App {
app := fiber.New()

// Register the custom validation function for 'fullname'
validate.RegisterValidation("fullname", validateFullname)

app.Post("/users", func(c *fiber.Ctx) error {
user := new(User)

if err := c.BodyParser(user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse JSON"})
}

if err := validate.Struct(user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}

return c.Status(fiber.StatusOK).JSON(user)
})

return app
}

// validateFullname checks if the value contains only alphabets and spaces.
func validateFullname(fl validator.FieldLevel) bool {
return regexp.MustCompile(`^[a-zA-Z\s]+$`).MatchString(fl.Field().String())
}

func main() {
app := setup()
app.Listen(":8000")
}

เมื่อลอง run Fiber ขึ้นมาและลองส่ง Request เข้าไปดูก็จะสามารถได้ผลลัพธ์ตามนี้ออกมาได้

fiber-01

code unit test fiber

ทีนี้ เมื่อมาถึงจังหวะต้อง Test สิ่งที่เราจะทำคือ

  1. ทำการรับตัว setup() fiber เข้ามาใน Unit test โดยไม่จำเป็นต้องเปิด port ออกไปด้านนอก (เนื่องจากแค่รับมาเพื่อ run test อย่างเดียว)
  2. ทำการสร้าง test case เพื่อตรวจสอบแต่ละเคสมาตั้งแต่
  • ส่งข้อมูลถูกต้อง = ตอบรับ 200 ถูกต้องหรือไม่
  • ส่ง email ไม่ถูก format = ตอบรับ 400 ถูกต้องหรือไม่ (API ไม่สำเร็จ)
  • ส่ง fullname ไม่ถูก format = ตอบรับ 400 ถูกต้องหรือไม่ (API ไม่สำเร็จ)
  • ส่ง age มาน้อยกว่า 0 = ตอบรับ 400 ถูกต้องหรือไม่ (API ไม่สำเร็จ)
main_test.go
package main

import (
"bytes"
"encoding/json"
"net/http/httptest"
"testing"

"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
)

// TestUserRoute function for testing the /users route.
func TestUserRoute(t *testing.T) {
app := setup()

// Define test cases
tests := []struct {
description string
requestBody User
expectStatus int
}{
{
description: "Valid input",
requestBody: User{"jane.doe@example.com", "Jane Doe", 30},
expectStatus: fiber.StatusOK,
},
{
description: "Invalid email",
requestBody: User{"invalid-email", "Jane Doe", 30},
expectStatus: fiber.StatusBadRequest,
},
{
description: "Invalid fullname",
requestBody: User{"jane.doe@example.com", "12345", 30},
expectStatus: fiber.StatusBadRequest,
},
{
description: "Invalid age",
requestBody: User{"jane.doe@example.com", "Jane Doe", -5},
expectStatus: fiber.StatusBadRequest,
},
}

// Run tests
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
reqBody, _ := json.Marshal(test.requestBody)
req := httptest.NewRequest("POST", "/users", bytes.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
resp, _ := app.Test(req)

assert.Equal(t, test.expectStatus, resp.StatusCode)
})
}
}

อธิบาย code เพิ่มเติม

  • ตอนใช้คำสั่ง t.Run() เพื่อ run ตัว unit test ออกมา เราจะใช้คำสั่ง json.Marshal() ในการแปลงข้อมูลให้มาเป็นข้อมูลจาก struct ของ Go (ที่เราทำเป็นตัวอย่างไว้) แปลงมาเป็น JSON format ได้ (เป็นการใช้คำสั่ง encoding/json ของ GO เพื่อจำลองให้เหมือนการส่ง JSON จริงๆเข้า Web server เข้ามาได้)
  • เหตุผลที่ต้องใช้คำสั่งนี้ เพื่อเป็นการจำลองการยิง Request จริงๆเข้ามาผ่าน HTTP request ที่จำเป็นต้องส่งเป็น JSON format เข้ามา (แทนที่จะเป็น format ของ Go)

ก็จะสามารถทดสอบผ่าน Fiber ได้ เมื่อลอง run ด้วย go test ก็จะได้ผลลัพธ์ของ Test ออกมาได้

PASS
ok mike 0.649s

หากลองแก้ไขเคสใดเคสหนึ่งให้ผิดไป

เช่นเคสนี้ เคส Age เราลองปรับให้เป็นจำนวนบวกดูแบบนี้

{
description: "Invalid age",
requestBody: User{"jane.doe@example.com", "Jane Doe", 5},
expectStatus: fiber.StatusBadRequest,
}

ซึ่งมันจะส่งผลทำให้การ run test ผิดได้ เนื่องจาก data นี้เป็น success case แต่ Testcase นี้เราดันบอกว่าจะต้องออกมาเป็น Fail case ตอน go test ออกมาก็จะเจอเป็น Error case ออกมาได้

--- FAIL: TestUserRoute (0.00s)
--- FAIL: TestUserRoute/Invalid_age (0.00s)
main_test.go:53:
Error Trace: /Users/tanitphonpaniwan/Repositories/projects/mikelopster/lab/go-testing/main_test.go:53
Error: Not equal:
expected: 400
actual : 200
Test: TestUserRoute/Invalid_age
FAIL
exit status 1
FAIL mike 0.705s

ก็จะสามารถบอกได้ว่ามีเคสไหนผิดออกมาได้