มีโอกาสมาแบ่งปันความรู้เรื่อง การพัฒนาระบบด้วยภาษา 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 ครับ