มีโอกาสมาแบ่งปันความรู้เรื่อง การพัฒนาระบบด้วยภาษา Go
ให้พัฒนาได้ง่าย
ให้ทดสอบได้ง่าย
ให้ดูแลได้ง่ายขึ้น
โดยสิ่งที่แบ่งปันประกอบไปด้วย

  • เรื่องของโครงสร้างที่ต้องชัดเจน
  • เรื่องของหน้าที่การทำงานของแต่ละส่วน
  • เรื่องแนวคิดในการทดสอบ เพื่อเพิ่มความมั่นใจที่มีต่อระบบ
  • เรื่องแนวทางการพัฒนาด้วยภาษา Go
  • เรื่องของเครื่องมือและ library ต่าง ๆ ที่นำมาใช้งาน

ในแต่ละ project หรือบางครั้งอาจจะแบ่งเป็น service เล็ก ๆ

สิ่งที่สำคัญมาก ๆ คือ โครงสร้างของระบบงานที่ชัดเจน
ในแต่ละระบบควรมีโครงสร้างไปในทิศทางเดียวกัน
เพื่อให้ง่ายต่อการพัฒนาและการดูแล (ยิ่งทำงานเป็นทีมยิ่งสำคัญ)
ผมจะอ้างอิงโครงสร้างมาจาก Microservices Testing
โดยโครงสร้างที่แนะนำก็ประกอบไปด้วย

  • Controller/API/Handler/Router ทำการรับ request และส่งผลการทำงานกลับไปยังผู้เรียก
  • Service/Business flow สำหรับจัดการ flow การทำงานตาม business
  • Repository/Data Access Object สำหรับการจัดการข้อมูลใน Database
  • Gateway สำหรับการติดต่อผ่าน HTTP/TCP protocol

โดยแต่ละส่วนนนั้นจะต้องมี interface คั่นเสมอ นั่นคือ
ControllerInterface -> Controller -> ServiceInterface -> Service -> RepositoryInterface -> Repository

อาจจะเยอะหน่อยนะ
แต่สิ่งที่ได้รับกลับมาคือ
สามารถแยกการทำงานแต่ละส่วนออกจากกันอย่างชัดเจน
ส่งผลให้สามารถแยกทดสอบได้ง่ายขึ้น
นั่นคือ ทำให้เรามีความมั่นใจต่อ code มากขึ้นด้วย

เมื่อแยกได้ ก็ต้องเขียน code ได้สิ !!

ดังนั้นมาลงมือเขียน code กันดวยภาษา Go
แยกการทดสอบเป็นแต่ละส่วน พร้อม library ที่ใช้งานดังนี้

Repository หรือ Data Access Object
ผมเลือกใช้งาน go-sqlmock สำหรับการจำลอง Database ที่ใช้งาน
ซึ่งช่วยให้การทดสอบมีความยืดหยุ่มมากขึ้น
เนื่องจากสามารถจำลองการทำงานของ Database ได้ทุกสถานการณ์
ทั้งการทำงานแบบปกติ
ทั้งการทำงานแบบ fail/error
ทั้งการค้นหาข้อมูลไม่เจอ
ทั้งข้อมูลไม่ถูกต้องตามที่ต้องการ

แต่สิ่งที่แนะนำได้คือ ถ้าสามารถควบคุม Database ได้
ก็ไม่จำเป็นต้องใช้พวก library เหล่านี้ก็ได้
เพราะว่า จะทำให้เรามั่นใจได้มากกว่า
หนึ่งในการควบคุมก็คือ การใช้ Container มาช่วย

Service หรือ Business flow
ผมเลือกใช้งาน Interface ปกติมาใช้งานไม่มีอะไรซับซ้อน
เนื่องจากในแต่ละส่วนจะคั่นด้วย interface อยู่แล้ว
ส่วนการสร้าง instance นั้น จะต้องผ่าน method NewXXXService() เท่านั้น
เพื่อกำหนดให้การสร้างมีรูปแบบเดียวกัน
จากนั้นในส่วนของการทดสอบก็ mock กันไปเลย

Controller หรือ API หรือ Handler หรือ Router
ในส่วนนี้สามารถทดสอบได้หลายแบบ
ทั้งการทดสอบเฉพาะการทำงานของ Controller
หรือจะทดสอบแบบ end-to-end ก็ได้ เอาที่สบายใจ
โดยสิ่งที่แนะนำไปคือ การทดสอบเฉพาะ Controller ก่อน
จากนั้นทำการตัด dependency หรือสิ่งที่ Controller ใช้งานออกไป

ซึ่ง Controller จะใช้งาน Service ต่าง ๆ
ดังนั้น จึงทำการตัด Service เหล่านั้นไปด้วยการ mock ผ่าน interface เช่นเดิม
แล้วทำการส่งหรือ Inject Service เหล่านั้นผ่าน Controller อีกที
ทำให้เราสามารถทดสอบการทำงานของ Controller ได้แล้ว
โดยไม่ต้องใช้ library ข้างนอกเลย

ในส่วนของการทดสอบนั้นได้นำ library testify มาใช้

เนื่องจากมี assertion ที่ใช้งานง่ายและอ่านสบาย
รวมทั้งมีเรื่องของ mock ให้ใช้ด้วย
หรือต้องการความสวยงามตาม style BDD ก็แนะนำ Ginkgo ได้

ปิดท้วยด้วยการ run test และดูค่า coverage ด้วย
เพื่อให้เรารู้ว่า code บรรทัดไหนที่ยังไม่ทำการทดสอบ

วันนี้เขียน test กันหรือยัง ?

โดย code ตัวอย่างและ slide อยู่ที่ github:Up1::go-testable

ขอให้สนุกกับการเขียน code ครับ