กระสุนด้าน: unit test ที่ทำงานช้า
posted on 25 Aug 2008 11:39 by wonam in softdevในปี 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 ได้เสนอแนวคิดและวิธีการแก้ไขส่วนชุดทดสอบ จากนั้นก็มีอีกหลายคนได้นำมาพัฒนาต่อ (ดูเช่นที่นี่)

จะเอาไปไว้ยิงมนุษย์หมาป่าแถวนี้บ้าง
#1 By LostOfCTRL on 2008-08-25 13:12