38777787

ช่วงไม่กี่วันเห็นพูดเรื่อง Dependency Injection (DI) ใน Go กัน
เลยลองมาศึกษาหน่อยว่า มันทำอย่างไร
และมีที่มาที่ไปอย่างไรบ้าง ซึ่งได้พบวิธีการบางอย่างที่น่าสนใจ
เลยนำมาเขียนสรุป ซึ่งเป็นเพียงวิธีการหนึ่งเท่านั้นนะ

ย้อนกลับไปในเรื่องแนวคิดของ TDD (Test Driven Development)
ช่วยลดเรื่องผลกระทบของ code ต่างๆ ของระบบได้
ถึงแม้ TDD จะไม่ได้แก้ไขปัญหาการออกแบบทั้งหมด
แต่ถ้าเคารพแนวคิดและปฏิบัติตาม
จะส่งผลให้ code ในระบบไม่ผูกมัดกัน และแยกหน้าที่การทำงานอย่างชัดเจน

ในปัจจุบันภาษาแบบ dynamic
ได้รับความนิยมอย่างมากมาย
ทำให้ unit test นั้นจำเป็นขึ้นมา

ทำไมล่ะ ?

ก็เพราะว่าภาษาแบบ dynamic มันพยายามแยกส่วนการทำงานต่างๆ
ออกจากกันอย่างชัดเจน โดยเชื่อมส่วนต่างเข้าด้วยกัน
ตามแนวคิด Inversion of Control (IoC)
โดยมีหลายหลาย framework ให้เลือกใช้งาน
แต่การทำงานเหมือนกัน นั่นก็คือ
พยายามเปลี่ยนแปลงพฤติกรรมของสิ่งต่างๆ ได้ตลอดเวลา ในช่วงของ Runtine นั่นเอง
ซึ่งปัญหามันอยู่ที่ตรงนี้แหละนะ ….

ลองคิดดูว่า ถ้าไม่มี unit test แล้ว เราจะรู้ได้อย่างไรว่า
มันทำงานกันอย่างไร  การทำงานที่ถูกต้องตามที่เราคาดหวังไว้เป็นอย่างไร
เนื่องจากสามารถแก้ไขพฤติกรรมได้เสมอ
ซึ่งส่วนใหญ่มักลืมคิดในส่วนนี้
จึงทำให้เกิดปัญหาในการดูแลรักษา
หลังจากที่ deploy ขึ้น production ไปแล้ว

ดังนั้นมาดูที่ Go บ้างเราสามารถใช้ความสามารถนี้ได้หรือไม่ ? อย่างไร ?
เริ่มด้วย  2 เรื่องสำหรับ Go ที่ต้องรู้และเข้าใจก่อนก็คือคุณสมบัติ 2 ข้อ
1. function คือสิ่งแรกของ Go เสมอ
2. Test สามารถเข้าถึงสมาชิกภายใน package ได้

จากคุณสมบัติทั้งสองข้อ ทำให้ code ของเรามันเล็กมาก
ไม่ผู้ติดกัน และสามารถเขียน unit test ได้อย่างง่ายดาย

เพื่อความไม่งง มาดูตัวอย่างกันดีกว่า

คำอธิบาย
จาก code เป็นการคำนวณค่าเฉลี่ย
โดยนำจำนวนคะแนน มาหารด้วยจำนวนสมาชิกทั้งหมด
function AverageScore() สามารถเข้าถึงได้จากนอก package
ส่วน function getAllMember() และ getTotalScore() ไม่สามารถเข้าถึงได้จากนอก package

จะสังเกตได้ว่า function AverageScore() นั้นมีความสัมพันธ์กับ
function getAllMember() และ getTotalScore()
ดังนั้นถ้าเราจะทำการทดสอบต้องทำการ Mock/Stub กันขึ้นมา
หรือถ้าในโลกของ OOP จะต้องสร้าง interface สำหรับ AverageScore ขึ้นมา
เพื่อให้สามารถสลับส่วน implementation ในการทดสอบได้

แล้วใน Go ล่ะทำอย่างไร ?
ทำการเปลี่ยน function getAllMember() และ getTotalScore() ไปเป็น variable ซะ
ตามนี้

ดังนั้นในตอนทดสอบหรือทำ unit testing
เราสามารถเปลี่ยนค่าของตัวแปร getAllMember  และ getTotalScore  ได้เลยในขณะทำการทดสอบ

เริ่มจากการทดสอบด้วยการเรียกใช้จากของจริง
สามารถเขียน unit test ได้ดังนี้

ต่อมาเราทำการ Stub ค่าของตัวแปร getTotalScore และ getAllMember ดังนี้

ผลที่ได้จากการทดสอบคือ result = 40 ตามที่เรากำหนดไว้

ยังไม่พอเรามาเล่นเกี่ยวกับ Mock โดยทำการตรวจสอบ
พฤติกรรมของการทดสอบว่าเข้าทำงานที่ ตัวแปร getTotalScore และ getAllMember
ตามใน unit test หรือไม่ ดังนี้

จาก code การ Mock นั้นจะสร้างตัวแปร 2 ตัวมาเพื่อตรวจสอบว่าแต่ละพฤติกรรมการทำงาน
มีการเข้าทำงานจริงๆ หรือไม่ นั่นเอง

จะเห็นได้ว่า code ทั้งหมดข้างต้นนั้น
สามารถเขียนได้จากหลากหลายภาษาโปรแกรม
ที่มี function เป็นส่วนการทำงานหลัก เช่นพวก Lambda  เป็นต้น

จะสังเกตได้ว่า Go นั้นอนุญาตให้ unit test
สามารถเข้าถึงตัวแปรที่เป็น private ของแต่ละ package ได้
ทำให้ code ในส่วนของ unit test ง่ายและสะดวกมากขึ้นเยอะเลย

จากตัวอย่างนี้ น่าจะเป็นอีกหนึ่งแนวทาง
สำหรับการพยายามสร้างสมดุล หรือ
นำข้อดีของภาษาพวก static และ dynamic มาใช้งาน

Reference Website
http://openmymind.net/Dependency-Injection-In-Go/
https://gist.github.com/karlseguin/8297287
http://golang.org/src/pkg/net/http/request_test.go