Tag Archives: test-first

Test first tools

เอา TDD ก่อน
Java – JUnit
.Net – NUnit
Ruby – Test::Unit
PHP – PHPUnit
Python – PyUnit, py.test
JavaScript – JSUnit

ตามด้วย BDD
Java – JBehave, Instinct หรือ JDave
.Net – NSpec
Ruby- RSpec
PHP – PHPSpec
Python – doctest
JavaScript – JSSpec

ที่เคยใช้จริงๆ คงจะเป็น JUnit, NUnit, Test::Unit และ RSpec ครับ
ฝั่ง ActionScript ของ flash ก็เคยครับ แต่ไม่อยากนับ เพราะแค่ลอง ไม่ได้อยู่กับมันนานๆ
ดูเหมือนภาษาที่ผมควรจะ implement ให้ดูจริงๆ ควรจะเป็น PHP สินะ

ปล. C กับ C++ ผมว่ามันเก่าไป ไม่น่าเอามาพูดถึง

Behavior Driven Development

จริงๆ เรื่องการ test บน Rails เนี่ย ผมกะว่าจะเขียนมานานละ
ช่วงนั้นมี Test Driven Development(TDD) กำลังแรงจาก Extreme programming ไอ้ผมก็เขียน test case บน Rails ก็ปกติดี แต่บังเอิญว่า Behavior Driven Development(BDD) เพิ่งเกิดพอดี เลยยังลังเลว่าจะเขียนเรื่องนี้เลยดีไหม

พอดีเจ้านายผมอยากให้เขียน test ในลักษณะที่รวมสิ่งแวดล้อมรอบด้านด้วย ใน 1 test case ของผมเลยค่อนข้างจะรุงรัง เช่น การ login ก็ตรวจมันตั้งแต่การ validate, login ผ่านหรือเปล่า สมมติว่าผ่าน ต้องจำค่า user เอาไว้แล้ว redirect ไปไหน?? หรืออีก test case นึง กรณีที่ไม่ผ่าน จะ redirect ไปไหน มันก็เลยดูเกินความหมายของคำว่า unit test ไปหน่อย

แล้วก็บังเอิญไปเจอ BDD พอดี ไปเจอ Rspec เข้าให้ ผมกับเจ้านายเลยคิดว่า ไอ้ตัวนี้แหละ ที่เราต้องการ !!! ผมก็เลยเอาไว้ก่อน ให้มันโตมากกว่านี้หน่อย ค่อยเขียน แล้วมันก็สุกงอมพอดี ตอนนี้กระแสที่รุนแรงที่สุดของ Rails คือการทำ BDD ด้วย Rspec นี่แหละ

ทำไมถึงต้องการ ??

BDD มันเกิดมาจากการเรียนรู้จาก TDD

โปรแกรมเมอร์ที่ใช้ TDD เริ่มจะมองเห็นมากกว่าการเป็น unit test คือ เห็นว่าการเขียน test ก่อนเขียน code จะทำให้เรารู้ว่า code ที่จำเป็นมันน่าจะเป็นยังไง หรือเห็น process ของงาน จากนั้นก็เริ่มเห็นความเป็นพฤติกรรม ซึ่งพวกเขาก็เชื่อว่าพฤติกรรมนี่แหละ เป็นหัวใจของ process

ทางทีมที่คิด BDD ขึ้นมา เลยเริ่มผลักดันให้มันเกิดขึ้นมา เพราะเห็นว่าโปรแกรมเมอร์ที่เขียน test คงจะมองเห็นถึงจุดการเป็นพฤติกรรมเนี่ย ไม่เยอะเท่าไหร่ อยากจะให้คนกลุ่มนี้ เข้าถึงแนวความคิดอันนี้

BDD จึงมีจุดประสงค์ให้โปรแกรมเมอร์หรือนักพัฒนา เน้นไปที่การมองว่ามันจะมีพฤติกรรมยังไงมากกว่าการ test ซึ่งจะช่วยให้เข้าถึงตัวงานจริงๆ ได้ตรงและเร็วที่สุด

ซึ่งแบบที่เจ้านายผมให้ผมเขียน test ใน test case นั่นแหละ มันดูเป็นพฤติกรรมตามแนวความคิดของ BDD เลย มันเลยโป๊ะเช้ะ

BDD มันมีพื้นฐานมาจากการดูที่พฤติกรรมเป็นหลัก ถ้าเราตั้งคำถามว่า

Login controller ควรจะทำอะไรได้บ้าง ?

ผมก็จะตอบว่า

มันควรจะ login ได้ปกติ
มันควรจะ login ได้ปกติ และจำไว้(remember me) ด้วย

มันควรจะ login ไม่ได้ เพราะ password ผิดหรือไม่มี username นั้นๆ
มันควรจะ logout ได้

เราจะมามองกันที่พฤติกรรมเป็นหลัก

ถ้าเราตอบได้หมดว่า Login controller ควรจะมีพฤติกรรมอะไรบ้าง นั่นก็น่าจะเป็นสิ่งที่เราต้องการและต้องพัฒนาทั้งหมดแล้ว เห็นภาพรวมหมดทั้งกระบวนการ

ถ้าจะให้กระบวณการ(พฤติกรรม)มันมีความถูกต้อง ก็คงต้อง test มัน(ก่อน)ละครับ สำหรับ Ruby/Rails คงต้อ Rspec นี่แหละครับ ที่ช่วยได้

ที่มา – blog ผมเอง

Test first is not hard

จาก post อันนี้ ผมก็เจอ comment 2 อัน คือ

ความหมายใช่เลย..แต่บางทีใน software requirement อาจจะระบุไม่ละเอียดมาก เลยทำให้พอถึงช่วง UAT ที่ผู้ใช้งานระบบ(ที่ละเอียดหน่อย) เข้ามาทดสอบแล้วบอกว่ายังขาดโน่น ขาดนี่ ทำงานไม่ได้ตาม business ต้องการ ก็ต้องกลับไปเริ่มต้นใหม่อีก

และ

เทสไปเทสมา พบว่ามีหลายอย่างที่ทาง developer ยังทำไม่เรียบร้อยเลยครับ ส่งมาได้ไง

มาดูว่า test first จะช่วยอะไรกับสองปัญหาข้างบนได้บ้าง

เริ่มแรกหลังจากได้ requirement มา programmer อย่างผม ก็ต้องอ่านๆๆ แล้วก็อ่าน แล้วก็สรุป ว่าต้องเขียนอะไรบ้าง(ผมจะเรียกว่า spec ละกัน หรือจะเรียกว่า test case ใน TDD ก็ได้) แล้วก็เขียนลงใน spec file

ตัวอย่างด้านล่างคือ Ruby code ที่ใช้ใน Ruby on Rails นะครับ
เป็นตัวอย่างการเขียน spec ทดสอบ login module ฝั่ง admin

describe AdminController, “authentication:” do
describe “handling POST /admin/login [PASSED]” do
it “should authenticate [PASSED]”
it “should not remember user”
it “should redirect to admin page”
end

describe “handling POST /admin/login [PASSED] with remember me” do
it “should authenticate [PASSED]”
it “should remember user”
it “should redirect to admin page”
end

describe “handling POST /admin/login [FAILED]” do
it “should authenticate [FAILED]”
it “should not remember user”
it “should render login page”
end

describe “handling GET /admin/login” do
it “should not authenticate user”
it “should render login page”
end
end

นี่คือ spec คร่าวๆ ของผม(ยังไม่ได้ implement spec ในส่วนนี้นะครับ) จะเห็นได้ว่ามันแสดงสิ่งที่ควรจะทำในทุกๆ กรณี อยากให้มันทำอะไร ใส่ไปให้มด ถ้า requirement ที่เราได้รับมามีไม่เพียงพอ หรือกำกวม มันจะโผล่มาตั้งแต่ตอนนี้ครับ แล้ว project manager ก็จะไปตีรันฟันแทงกับลูกค้าเอง ในการทำให้มันชัดเจน แล้วก็กลับมาเขียน spec ใหม่อีกที

ปัญหาใน comment ข้อแรก มันจะโผล่มาตั้งแต่ก่อน coding ไม่ต้องรอไปจนถึงการทำ UAT ครับ เริ่มต้นใหม่เหมือนกัน แต่ก็ไม่ต้อง code ให้เสียแรง และไม่ต้อง test ให้เปลืองพลัง

หลังจากนั้น ผมก็ต้อง implement ในส่วนของ spec(testing) กับ business logic และต้อง test ให้ spec ผ่านหมดทุกกรณี ถ้าเราเขียน spec ละเอียด และ test ให้ผ่านหมดทุกกรณี ปัญหาใน comment ข้อที่สอง ก็จะหมดไป(ต้องให้ผ่านหมดทุกกรณีนะครับ และ programmer ก็ห้ามหลอกตัวเองด้วย เหอๆ)

test ผ่านหมด ก็แสดงว่า implement หมดพอดี ต่อไปก็จัดการฝั่งหน้าตาให้มันสวยงาม เราก็ไม่ต้องไปกังวลกับฝั่ง business logic ให้มากความ ถึงต้องกลับไปซ่อม ก็ไม่เหนื่อยมาก

แต่ก็ใช่ว่าเขียน test แล้ว จะไม่มีปัญหาเลย ยังไงๆ ก็ยังมีปัญหาที่คาดไม่ถึงอยู่อีก แต่มันก็ทำให้ฝั่ง tester ทำงานได้สบายขึ้น เวลา test ก็น้อยลง งานก็เสร็จเร็วขึ้น(ถ้าไม่ไปตายตอนเรียนรู้การเขียน test เสียก่อน)

Test first ที่ผมใช้งานอยู่ ไม่ยากเลย 🙂

ปล. ผมใช้ BDD ครับ คำศัพท์อาจจะไม่ค่อยคุ้นเท่าไหร่ ไว้ค่อยๆ เขียนอีกทีในครั้งต่อๆ ไป
ปอ. ผมจะลอง BDD ฝั่ง PHP ละกัน ดูเหมือนจะไม่ค่อยมีใครใช้ Ruby :p
ปฮ. เอ๊ะ ถ้าคนเขียน spec เป็น tester จะเขียนได้ละเอียดกว่า programmer ไหม??

Programmer can test

programmer ก็ช่วย test ได้ครับ

ผมทำงานในทีมเล็กๆ ไม่มีเงิน หรือคนมากพอที่จะไปจ้าง หรือสร้างทีม tester เพราะฉะนั้น โปรแกรมเมอร์นี่แล จะต้องแบ่งเบาภาระการ test ให้กับทีม

สิ่งที่ programmer ช่วยได้ คือการใช้ Test driven development(TDD) เป็นตัวช่วย

สิ่งที่ผมเคยฟาดฟันมาก่อน คือการใช้ unit test ทำการ test source code ในทุกๆ unit ไม่ว่าจะเป็นตัวแปรเอย method(หรือบางคนจะเรียกว่า function)เอย หรือ integration test ที่เขียนยังไงก็ไม่ครอบคลุม จนมีฝรั่งใจดี สร้างสิ่งที่เรียกว่า Behavior driven development(BDD) นี่แหละ ใช่เลย แทนที่เราจะมองให้เป็น unit ทำไมเราไม่มองให้เป็นพฤติกรรม(behavior) ทดสอบที่พฤติกรรมไปเลย มันก็เลยเป็นการรวมกันของ unit test กับ integration test นี่แหละ ใช่เลย!!

แล้ว programmer จะไปเขียน test ตอนไหน ?? หลายๆ คนที่เคยทำ ก็อาจจะบอกว่า ก็เขียน code ไปก่อน แล้วไปเขียน test ทีหลังไง แต่ผมไม่ได้ทำแบบนั้น ผมใช้แนวคิดเรื่อง test first เป็นแนวทาง ก็คือเขียน test ไปก่อนนั่นแหละ แล้วค่อย implement ทีหลัง จริงๆ ต้องบอกว่า test ไป implement ไป มากกว่า

ตอนที่ใช้ Unit test มันจะทำ test first ยากหน่อย เพราะเวลา test มันจะต้อง test จาก code ที่เรา implement จริงๆ แต่พอได้ BDD มาช่วย การทำ test first ก็ดีขึ้น เพราะมันมีสิ่งที่เรียกว่า Mock/Stub เข้ามาช่วย เราเขียน test ได้นานขึ้น แล้วค่อยไป implement ทีเดียว

แน่นอนว่า การทำ test first จะช่วยให้การ test ง่ายขึ้น ดัก runtime error หรือ bug ที่เราคาดไม่ถึงมาก่อนได้มากขึ้น ถ้าเราใช้ unit test มันก็คงจะ test ได้แค่ unit นั้นๆ ที่เราพิจารณา ถึงแม้ integration test จะ test โดยรวมได้ แต่ผมก็ยังว่ามันไม่ครอบคลุมอยู่ดี ถ้าใช้ BDD จะช่วยเรื่องนี้ได้เยอะ เพราะเรามองเป็นพฤติกรรม(เดี๋ยวมาลุยกันตอนถัดไปครับ) ถ้าพฤติกรรมที่เรากำลัง test ผ่าน runtime error ก็จะหายไปเยอะเลย

ฟังดูเหมือนว่าการเขียน test จะเป็นสิ่งดี แต่มันไม่ค่อยสนุกเท่าไหร่ ปัญหามันก็มีบ้างครับ ส่วนใหญ่จะเป็นเรื่องของเวลาครับ เพราะเราจะเสียเวลาไปกับการเขียน test เพิ่มขึ้น แล้วเวลา coding เราจะน้อยลง และต้องเสียเวลาในการเรียนรู้อยู่นานครับ แต่ก็ยังพอมีสิ่งที่ผมเห็นว่าเป็นข้อดีในข้อเสียนี้ ก็คือ เราเสียเวลาในการเขียน code ก็จริง แต่ถ้าเขียน test ให้ครบหมดทุกกรณี(โดยเฉพาะ BDD คือ ทุกกรณีของพฤติกรรมของงานของเรา) เราจะมองเห็นทุก requirement และเป็น flow chart แบบคร่าวๆ

โดยส่วนตัวแล้วผมว่าเสียเวลาสักหน่อย กับการเขียน test เพื่อแลกกับความถูกต้องของงาน ผมว่าคุ้มครับ ทั้งนี้ ยังช่วยให้ tester ทำงานได้ง่ายขึ้นด้วย