ในปี 1986  Fred Brooks หนึ่งในผู้บุกเบิกด้านวิศวกรรมซอฟต์แวร์ได้เขียนบทความชื่อ "ไม่มีกระสุนเงิน" (No Silver Bullet อ่านต้นฉบับ)  ในบทความดังกล่าวได้พูดถึงกระสุนเงินที่จะมาช่วยปราบปีศาจร้ายหมาป่าที่น่าสะพรึงกลัว ที่ชื่อว่า "การพัฒนาซอฟต์แวร์"   กระสุนเงินที่เมื่อยิงทะลุหัวใจก็ทำให้ปีศาจร้ายตายลงในทันที เปรียบเทียบได้กับเครื่องมือหรือกระบวนวิธีที่เมื่อนำมาใช้จะสามารถจัดการเปลี่ยนการพัฒนาซอฟต์แวร์ให้กลายเป็นเรื่องกล้วย ๆ ได้เลย

Brooks กล่าวว่า อย่าไปหามันเลย มันไม่มีหรอก กระสุนเงินดังกล่าวน่ะ

ในการให้เหตุผลของ Brooks เขาเริ่มจากการตั้งคำถามว่าทำไมการพัฒนาซอฟต์แวรจึงยาก?  แน่นอนสาเหตุก็เพราะว่ามันสลับซับซ้อนนั่นเอง เขาจึงเริ่มพิจารณาที่ความซับซ้อนในการพัฒนาซอฟต์แวร์ดังกล่าว

Brooks ได้แบ่งความซับซ้อนออกเป็นสองกลุ่มคือ ความซับซ้อนที่ไม่จำเป็น (accidental complexity) กับความซับซ้อนแก่นแท้ (essential complexity)   ประเด็นก็คือเราสามารถมีเครื่องมือที่ใช้จัดการทำลายความซับซ้อนที่ไม่จำเป็นได้ แต่ความซับซ้อนที่เป็นแก่นแท้ (ซึ่งมีมากมาย) นั้น เราไม่สามารถจะหลีกเลี่ยงได้

เขาได้อธิบายว่าในการพัฒนากระบวนวิธีและเครื่องมือในการพัฒนาซอฟต์แวร์นั้น เราแทบจะได้ทำลายความซับซ้อนที่ไม่จำเป็นไปจนหมดแล้ว โดยเครื่องมือดังกล่าวที่ Brooks ได้ให้รายการมาก็คือ

  • ภาษาขั้นสูง
  • ระบบแบ่งเวลา (Time sharing) --- เมื่อเขียนโปรแกรมแล้วสามารถคอมไพล์ เรียกใช้งานและทดสอบได้ทันที เมื่อเทียบกับการเขียนแล้วต้องรอส่งโปรแกรมไปคอมไพล์ รอส่งไปทำงาน ในระบบเมนเฟรมสมัยก่อน
  • รูปแบบในการโปรแกรมที่เหมือน ๆ กัน (Unified programming environment)

ที่เขียนไล่เรียงความมาตั้งนานเพื่อจะเล่าเกี่ยวกับ unit testing

วันก่อนผมนั่งเขียนโปรแกรม แล้วก็พยายายเขียนแบบใช้การทดสอบเป็นตัวขับเคลื่อน  ทีนี้ระหว่างที่เขียนบังเอิญไม่มี net ใช้ก็เลยใช้แค่เครื่องมือและเอกสารที่พอมีอยู่ในเครื่อง

ทีนี้ เครื่องมือในการทำ unit testing ที่มีมาให้อัตโนมัติ มันทำงานค่อนข้างช้า กว่าจะทำงานเสร็จก็กินเวลาหลายสิบวินาที

ยิ่งทำไป ยิ่งพบว่า เวลาหลายสิบวินาทีนี้ช่างเป็นตัวถ่วงการเขียนของเราจริง ๆ  แทนที่จังหวะของ cycle การเขียนเทส เขียนโค้ด ทดสอบจะไปได้อย่างต่อเนื่อง มันก็จะมากระตุก ๆ อยู่

แน่นอนว่าเวลาสิบวินาทีนี่ไม่นานเลย แต่มันก็ทำให้เสียจังหวะ (นั่นคือต้องมีการรอ) 

ผมก็เลยได้พบกับตัวว่าที่หลาย ๆ คน (ที่ผมนึกออกก็คือ Jay Fields) กล่าวว่าเงื่อนไขสำคัญอันหนึ่งของการทำ unit test ก็คือความเร็วนั่นเอง

ถ้าการทดสอบนั้นช้าเกินไปจนทำลายจังหวะในการพัฒนาโปรแกรม ก็เหมือนกับว่ากระสุนเงินอันเก่าแก่ (ส่วน time-sharing) นั้นถูกทำให้ด้านไปนั่นเอน

หลายคนอาจจะสงสัยว่า unit test มันทำงานกับแค่ส่วนเดียวของโปรแกรม จริง ๆ ก็น่าจะทำงานได้เร็วอยู่แล้ว ทำไมถึงได้มีปัญหา

สำหรับตัวผมเองที่เจอปัญหาก็คือการพัฒนาบน Ruby on Rails ซึ่งชุด unit test ที่มีมาให้มันค่อนข้างใหญ่โต รวมถึงมีการติดต่อกับ database ด้วย ทำให้กว่าจะโหลดขึ้นก็กินเวลาไปนานโข

ทีนี้จะทำอย่างไรให้ unit test มันทำงานได้เร็ว?

วิธีการถ้าจะกล่าวง่าย ๆ ก็คือพยายามตัดส่วนที่มันทำงานช้าที่ไม่จำเป็นออก โดยมากแล้วส่วนที่ช้าก็คือส่วนที่ unit ที่เราต้องการทดสอบไปเกี่ยวข้องกับส่วนอื่น ๆ (หรือที่เรียกว่า dependency นั่นเอง)    การตัดดังกล่าวทำได้โดยการเขียนส่วนที่เกี่ยวข้องขึ้นมาใหม่ให้พอทดสอบได้ (เขียน stub) หรือไม่ก็ใช้ของปลอม (mock) ในการทดสอบเสียเลย

ในกรณีของ Rails นั้นได้มีพัฒนาการในส่วนของการทดสอบดังกล่าว โดย Jay Fields ได้เสนอแนวคิดและวิธีการแก้ไขส่วนชุดทดสอบ จากนั้นก็มีอีกหลายคนได้นำมาพัฒนาต่อ (ดูเช่นที่นี่)

Comment

Comment:

Tweet

Tux-Linux: แก่นแท้ของเขา ผมว่ามันไม่แก่นแท้เชิง np-hardness อ่ะมั้ง น่าจะแบบ ซับซ้อนเพราะต้องคุย อะไรพวกนี้น่ะ

คุณ deans4j: อ้า.. น่าลองครับ

#11 By wonam on 2008-09-03 11:14

ครับผม ในกรณีของ Grails เองจะแยกชัดเจนให้เลยระหว่าง unit/integrate test

http://grails.org/Unit+Testing

เค้ายกตัวอย่างกรณีของเวทย์มนต์ที่ได้จากการสังเคราะห์ method ในส่วนของ domain class จะมีให้เฉพาะใน integrate test เท่านั้น

#10 By deans4j (124.120.133.237) on 2008-09-01 02:14

สงสัยผมจะเข้าใจผิดว่า "ความซับซ้อนแก่นแท้" คือพวก NP problem นะครับ

#9 By Tux-Linux (203.159.36.13) on 2008-08-31 09:46

ขอบคุณสำหรับ comment นะครับ

plug.in: เดี๋ยวโดยยิงกลับ
คุณ plynoi: ผมก็ลืมไปเลยครับ จนกระทั่งมานึกได้อีกเมื่อเร็ว ๆ นี้
คุณ xinnix: แถมเสียอารมณ์อีกต่างหากครับ
veer: ผมจะลองเล่น grails แล้ว อิอิ
rainam: จริง ๆ ไม่ค่อยรู้อะไรหรอกครับ กำลังศึกษาครับ
Tux-Linux: ความซับซ้อนแก่นแท้เป็นไงอ่ะ?
คุณ deans4j: ตัว unit test ที่มากับ rails มันดันไปยุ่งกับ database น่ะครับ เพราะว่าเค้ากะจะให้ใช้ test model อ่ะครับ มันเลยมี fixtures ให้อัตโนมัติอ่ะครับ หลัง ๆ ผมเลย mock แหลกเลยเหมือนกันครับเพื่อความเร็ว แต่วันนั้นที่ทำมันไม่มีหนังสือ/คู่มืออยู่ข้าง ๆ on-line ก็ไม่ได้น่ะครับ ;)

#8 By wonam on 2008-08-30 18:44

อย่างที่ อ. ทำตอนแรก น่าจะไปผสม integrate test เข้าไปด้วยแน่เลยครับ :)

โดย concept แล้วถ้า unit test จริงๆ ไม่จำเป็นต้อง load framework ขึ้นมาก็ควรจะใช้ได้แล้วครับ

ใช้การ mock dependency เอาหมด :D

ถ้าทำ TDD แบบพวก mockist จ๋าๆ เค้าจะ unit test แทบทุก unit เลยครับ

แต่ถ้าไม่ใช่ก็จะ driven จาก integrate test ก็ได้ครับ

ผมเป็นประเภทสอง (ไม่ใช่สาวประเภทสอง) ครับ จะ unit test ตามเห็นว่าซับซ้อนพอจะทำให้ผมกลัว

#7 By deans4j (124.120.134.73) on 2008-08-29 07:46

ช่วงหลังไม่ได้ทำงาน software เลย
แต่ก็ได้รู้แล้วว่า "ความซับซ้อนแก่นแท้" มันสุดจริงๆ (เราคิดว่าอาจารย์หมายถึง NP ต่าง) และเพิ่งเข้าใจคำที่อาจารย์เคยสอนในห้องว่า "พยายามใช้ชีวิตอยู่ห่างๆ NP ให้มากที่สุด"

#6 By Tux-Linux (117.47.12.195) on 2008-08-27 21:10

อ่านแล้วก็ทึ่งๆ อยากเก่งๆ เหมือนมะนาวจังเลย big smile

ทำงานเอกชน ตอนนี้รู้สึกเหมือนโง่ลงๆ (และก็โหดขึ้นๆ)

#5 By rainam on 2008-08-27 19:09

เจ๋ง ... ผมยังไม่ได้เขียน test ใน pylons เลย T_T

#4 By veer on 2008-08-25 21:53

เรื่องเสียจังหวะนี้ท่าจะจริง

เห็นด้วยครับ

#3 By xinnix on 2008-08-25 20:23

No silver bullet
จำได้ว่าเคยต้องอ่านสมัยเรียน Software eng
ชื่อนี้หายไปจากความทรงจำมาจะ 6 ปีแระ sad smile

#2 By plynoi แว่วศรี on 2008-08-25 16:43

จาน ขอกระสุนซัก 2 - 3 นัดซิครับ

จะเอาไปไว้ยิงมนุษย์หมาป่าแถวนี้บ้าง sad smile

#1 By CyberAlchemist on 2008-08-25 13:12