ภาษา Go ตอน 7 สอนเขียน Unit Test
ในสมัยเรียน เพื่อนๆ น้องๆ อาจจะเคยเห็นว่า อาจารย์ก็พูดอยู่อย่างสม่ำเสมอ ว่าเวลาเขียนโปรแกรมเสร็จแล้ว เราต้องเขียน Unit Test ของ Method นั้นด้วยนะ Function นั้นด้วยนะ
เพื่อที่จะได้ตรวจสอบว่าการทำงานของ Function หรือ Method ที่เราเขียนมานั้นมันยังทำงานได้เหมือนเดิม
แต่ว่าไปแล้ว ตอนสมัยเรียน ก็อาจจะไม่ได้เขียนตามที่อาจารย์บอก เราเพียงแค่รู้ว่าต้องทำ แต่อาจจะไม่เคยทำ หรือ อาจจะไม่รู้เลยว่ามันเริ่มต้นเขียนกันยังไงนะไอ Unit Test เนี่ย
ดังนั้นในบทความนี้ ผมจะพาไปทำความรู้จักกับ Unit Test ว่ามันคืออะไร มีความจำเป็นยังไง และ เราจะเริ่มต้นเขียนมันได้ยังไง แบบ Step by Step
Unit Test คืออะไร ?
Unit Test คือ 1 ในขั้นตอนของกระบวนการพัฒนาซอฟต์แวร์ ที่ไม่ว่าเราจะไปใช้ รูปแบบการพัฒนาแบบไหนก็ตาม เราก็ต้องมี Unit Test เสมอ
Unit Test จะเริ่มทำการเขียน Code เพื่อทดสอบส่วนที่เล็กที่สุดของโปรแกรมเรา โดยไอส่วนที่เล็กที่สุดนั้นหละเราเรียกว่า Unit หรือพูดง่ายๆ ก็พวก Method, Function ที่เราสร้างกันเอาไว้
Unit Test จะช่วยให้เพิ่มความมั่นใจให้กับพวกเรา ในการแก้ไขตัว Source Code ไม่ว่าจะแก้ หรือ Refactor Code และกลับมา Run Unit Test แล้วโปรแกรมผ่านทุก Case ที่เราตรวจสอบเอาไว้ เราก็จะสบายใจได้ ว่าโปรแกรมเรายังทำงานได้เหมือนเดิม
โดยจริงๆแล้ว Unit Test อยู่ในส่วนหนึ่งของการพัฒนาโปรแกรมแบบเขียน Test ก่อนเขียนเสมอ เรียกว่า (Test Driven Development: TDD)
Test Driven Development (TDD) คืออะไร ?
Test Driven Development คือส่วนหนึ่งของรูปแบบการพัฒนาซอฟต์แวร์ของ Agile Software Development
รูปแบบการเขียน TDD มีหลักการคร่าวๆ ประมาณว่า ก่อนที่จะเริ่มเขียน TDD ได้นั้นเราจะต้องรู้เกี่ยวกับ Requirement มาเพียงพอก่อนระดับนึง คือพูดง่ายๆว่า เรารู้แล้วว่าเราจะเขียนโปรแกรมนี้ไปทำไม
พอเรารู้ Requirement ได้ระดับนึงแล้ว ก่อนที่จะไปเขียน Code ตาม Requirement เราจะมาออกแบบ Test Case กันก่อน ว่ามันควร Test อะไรบ้าง
เมื่อออกแบบ Test Case เสร็จแล้ว เราจะ Run Test เมื่อ Run Test เสร็จเราก็จะเห็นว่า มันไม่ผ่าน Test Case ที่เราสร้างไว้ ก็แน่นอน มันต้องไม่ผ่าน ก็เรายังไม่ได้เขียน Code นิ ใช่ไหมครับ
พอ Test Case ที่เรา Run Test ไปไม่ผ่าน เราก็อ่าน Error จาก Test Case นั้น แล้วก็ค่อยลงมือเขียน Code เมื่อ เขียน Code เสร็จ เราจะ Run Test อีกครั้ง เมื่อครั้งนี้ Run Test ผ่าน
เราจะเริ่ม Refactor Code ที่เราเพิ่งเขียนไปเมื่อกี้ การ Refactor Code เราก็คือ การทำให้ Code เรามันมีหน้าตาที่ดูดีขึ้น สามารถอ่านได้ง่าย และ ปรับแต่งให้มีประสิทธิภาพที่สุด
เมื่อ Refactor Code เสร็จแล้ว เราจะ Run Test อีกรอบ และเมื่อ Run Test Case ทั้งหมด ก็ควรจะผ่านเหมือนเดิม
เมื่อผ่านแล้ว เราก็ค่อยเริ่มเขียน Test Case ตัวต่อไป จนหมดความต้องการของ Requirement
เริ่มต้นเขียน Unit Test ของภาษา Go กัน
ผมจะพาทำแบบ Step by Step และใช้การเขียนโปรแกรมแบบ TDD ในการเขียนโปรแกรมครั้งนี้ ไปลุยกันเลยครับ
Step 0 : พื้นฐานที่ต้องมีในภาษา Go
Step 1 : ดู Requirement
โจทก์วันนี้ที่เราจะเขียนโปรแกรมกันก็คือ โจทก์ปัญหา FizzBuzz โดยโจทก์นี้จะให้เราแก้ปัญหาดังนี้
- อะไรก็ตามที่หาร 3 ลงตัว ให้แสดงผลเป็น Fizz
- อะไรก็ตามที่หาร 5 ลงตัว ให้แสดงผลเป็น Buzz
- อะไรก็ตามที่หาร 15 ลงตัว ให้แสดงผลเป็ร FizzBuzz
- ถ้าไม่เข้า 3 เงื่อนไขข้างบนให้แสดงตัวเลข ที่ใส่เข้าไปออกมา
Step 2 : สร้าง Project FizzBuzz
2.1 เข้าไปที่ Path
/Users/chaiyarin/go/src/github.com/ชื่อ Account Github ตัวเอง
2.2 สร้าง Directory ชื่อ fizzbuzz
mkdir fizzbuzz
2.3 สร้างไฟล์ fizzbuzz.go ขึ้นมา และประกอบไปด้วย Source Code ดังนี้
[FileName: fizzbuzz.go]package fizzbuzz// Fizzbuzz : fizzbuzz function
func FizzBuzz(number int) string { return "";
}
Step 3 : เริ่มต้นเขียน Unit Test
มาถึงตอนนี้เรารู้แล้วว่าเราจะ เขียนโปรแกรม FizzBuzz และมีเงื่อนไขอะไรบ้าง ดังนั้น ก่อนที่เราจะไปเขียนโปรแกรม เรามาเขียน Test กันก่อนครับ โดยเริ่มจาก
3.1 สร้างไฟล์ fizzbuzz_test.go ที่ Path โปรเจค fizzbuzz โดยการตั้งชื่อไฟล์ที่เป็นไฟล์ประเภท Test จะอยู่ในรูปแบบ “ชื่อไฟล์ที่เราจะเขียนทดสอบ_Test.go”
/Users/chaiyarin/go/src/github.com/ชื่อ Account Github ตัวเอง/fizzbuzz
3.2 ในไฟล์ fizzbuzz_test.go ประกอบไปด้วย Source Code ดังนี้
[Filename: fizzbuzz_test.go]package fizzbuzz // --> (1)import (
"testing" // --> (2)
)func TestInput1ShouldBeDisplay1(t *testing.T) { // --> (3) v := Fizzbuzz(1) // --> (4) if "1" != v { // --> 5
t.Error("fizzbuzz of 1 should be '1' but have", v) // --> (6)
}}
อธิบาย Source Code
(1). คือการนำ Package fizzbuzz ที่เราสร้างไว้ในไฟล์ fizzbuzz.go มาใช้งาน
(2). คือการนำ Package ของ Go เกี่ยวกับการ Test มาใช้งาน
(3). เขียน Function ที่เป็นชื่อ Test Case ของเรา แล้ว “t *testing.T” คือ Parameter สำหรับใช้ในการ Test เป็น Syntax สำหรับการ Test ของ GO
โดย TestCase เราตั้งชื่อว่า “TestInput1ShouldBeDisplay1”
(4). เรียก Function FizzBuzz นำมาใส่ Parameter เป็นเลข 1 เพื่อเราจะทดสอบ Case แรกของเราว่า ถ้าใส่ 1 เข้าไปใน Function FizzBuzz แล้ว จะต้องได้เลข 1 ออกมา เก็บไว้ที่ ตัวแปร v นะ
(5). คราวนี้เราก็จะเอาค่า v มา เช็คว่ามันเท่ากับ 1 ที่เราต้องการหรือเปล่า ถ้าเท่า TestCast ที่เราสร้างนี้ก็จะผ่านแบบสวยๆ แต่ถ้ามันไม่เท่า มันก็จะเข้า If ของเราตัวนี้
(6). และมันจะบอกเราว่า เห้ย ถ้ามันไม่ใช่ 1 และมันได้ค่า v เป็นอะไรกลับมากันนะ ผ่าน Console ที่เรา Run Test
ก่อนจะไป Step 4 เรามาเช็คกันหน่อยว่าเราตามมาอยู่จุดๆเดียวกันแล้ว
โดย ตอนนี้ภายใน Path
/Users/chaiyarin/go/src/github.com/ชื่อ Account Github/fizzbuzz
จะประกอบไปด้วย 2 ไฟล์ด้วยกัน คือ 1.fizzbuzz.go และ 2.fizzbuzz_test.go นะครับ ถ้าตรงกันแล้วเราไป Step 4 กันต่อเลย
Step 4 Run Test
เปิด Terminal หรือ Console หรืออะไรที่ใช้ Run Go มาที่ Path เดิมที่เราอยู่เลยครับ
/Users/chaiyarin/go/src/github.com/ชื่อ Account Github/fizzbuzz
และสั่งด้วยคำสั่ง
go test
สั่งแล้วจะได้หน้าตาแบบนี้
อธิบายก็คือ มันแจ้งว่า Test Case “TestInput1ShouldBeDisplay1” มันควรจะได้ 1 แต่ว่า มันได้ค่า “ว่าง” ออกมา
มันเลยบอกว่า Test Case นี้ไม่ผ่านนะ ไปแก้ Code ในไฟล์ fizzbuzz.go ซะ
Step 5 แก้ Code ใน fizzbuzz.go
จากการ Run Test ผลลัพธ์มันแจ้งว่า Test Case เราไม่ผ่านเพราะ เมื่อเราใส่ 1 ไปใน Test Case “TestInput1ShouldBeDisplay1” มันควรจะได้ “1” แต่มันได้ ค่าว่าง ออกมา ดังนั้นเราก็จะแก้ Code ในไฟล์ fizzbuzz.go แบบนี้
package fizzbuzz// Fizzbuzz : fizzbuzz function
func Fizzbuzz(number int) string { // Fizzbuzz : fizzbuzz function return "1" // --> เปลี่ยนจากค่า "ว่าง" เป็น 1 ซะ}
ให้เราเปลี่ยน return จากค่าว่าง ให้ return เป็น 1 ซะ และเดี๋ยวลอง Run Test ใหม่
Step 6 Run Test อีกครั้ง
โดยใช้คำสั่ง
go test
และคราวนี้ Test เราก็จะผ่านแล้ว ดังภาพนี้
Step 7 Refactor Code ใน fizzbuzz.go
แต่ ณ ตอนนี้ยังไม่มีอะไรให้ Refactor เลยต้องข้าม Step 7 นี้ไปก่อน
Step 8 เขียน Test Case เพิ่ม
เข้าไปที่ fizzbuzz_test.go แล้วเพิ่ม Test Case ไปแบบนี้
[Filename: fizzbuzz_test.go]func TestInput2ShouldBeDisplay2(t *testing.T) { v := Fizzbuzz(2) if "2" != v {
t.Error("fizzbuzz of 2 should be '2' but have", v)
}}
อธิบาย Test Case นี้ได้ว่า เมื่อใส่ 2 ไปใน Function Fizzbuzz ควรจะแสดง 2 ออกมา เพราะไม่เข้า Case อะไรเกี่ยวข้องกับ fizzbuzz เลย
Step 9 Run Test
สั่ง Run Test ใหม่
go test
เมื่อสั่ง Run Test ผลลัพธ์ที่ได้มันก็จะ Fail อีก ดังภาพ
ที่มันแสดงผล Fail ก็เพราะว่า เมื่อเราใส่ 2 ไป มันกลับแสดงค่า 1 ออกมา เพราะว่า ในไฟล์ fizzbuzz.go เราบังคับให้มัน Return เป็น 1 อยู่
Step 10 ไปแก้ไข Code ในไฟล์ fizzbuzz.go
[Filename: fizzbuzz.go]package fizzbuzz// Fizzbuzz : fizzbuzz function
func Fizzbuzz(number int) string { if number == 1 {
return "1"
} return "2"
}
Step 11 Run Test ใหม่
สั่ง Run Test
go test
คราวนี้ Test Case เราก็จะผ่านแล้ว
สรุป การเขียน Unit Test ในภาษา Go
คือ จากตัวอย่างที่ผมเขียนให้ดู เราก็จะทำแบบนี้วนลูปไปเรื่อยๆ จนกว่า โปรแกรม FizzBuzz เราจะเสร็จ โดย Loob ของการเขียน TDD มันจะเป็นในลักษณะ
1.เขียน Test -> 2.Run Test -> 3.แก้ code -> 4.Run Test -> 5.Refactor Code -> 6.Run Test และ กลับมาข้อ 1 ใหม่อีกครั้ง
เหมือนแผนภาพที่แสดงไว้ใน หัวข้อ Test Driven Development
สามารถดู Source Code Test Case ที่เขียนเสร็จแล้ว ของโปรแกรม FizzBuzz ได้จาก Link Github ข้างล่างนี้ครับผม
แล้วเจอกัน ตอนที่ 8 นะครับ คือก็ตอน “มาลองทำ RestFul Api ด้วย ภาษา Go” บ๊ายๆ