การถกเถียงเรื่องการทดสอบ: การทดสอบอัตโนมัติคือเครื่องช่วยชีวิตหรือแค่การสูญเสียเวลา?

ทีมชุมชน BigGo
การถกเถียงเรื่องการทดสอบ: การทดสอบอัตโนมัติคือเครื่องช่วยชีวิตหรือแค่การสูญเสียเวลา?

ในโลกของการพัฒนาซอฟต์แวร์ มีหัวข้อไม่กี่เรื่องที่สามารถจุดประกายการถกเถียงอย่างเร่าร้อนได้เท่ากับคุณค่าของการทดสอบอัตโนมัติ ขณะที่นักพัฒนาบางคนยึดมั่นใน Test-Driven Development (TDD) และชุดการทดสอบที่ครอบคลุม นักพัฒนาอีกกลุ่มกลับมองว่าสิ่งเหล่านี้เป็นการสูญเสียเวลาอย่างมหาศาล ความแตกแยกนี้ถูกเน้นย้ำไม่นานมานี้ในการอภิปรายออนไลน์ที่มีชีวิตชีวา ซึ่งนักพัฒนาได้แบ่งปันประสบการณ์ที่ขัดแย้งกันอย่างชัดเจนจากสนามจริง การสนทนาดังกล่าวเผยให้เห็นความตึงเครียดที่ฝังลึกระหว่างแนวปฏิบัติที่ดีตามทฤษฎีและความเป็นจริงที่ยุ่งเหยิงของการส่งมอบโค้ด

ข้อโต้แย้งต่อต้านการทดสอบ

นักพัฒนาส่วนใหญ่ยังคงสงสัยในผลตอบแทนจากการลงทุนในการทดสอบอัตโนมัติ นักพัฒนารุ่นเก๋าที่มีประสบการณ์เกือบ 20 ปี เปิดเผยความรู้สึกที่สะท้อนกับหลายคน โดยระบุว่าพวกเขาไม่เคยพบว่าการทดสอบอัตโนมัติจะคุ้มค่ากับเวลาอันมหาศาลที่ต้องเสียไปเลยแม้แต่ครั้งเดียว มุมมองนี้มักมาจากผู้ที่ต้องทนกับการบำรุงรักษาการทดสอบที่เปราะบางและออกแบบมาไม่ดี ซึ่งพังลงทุกครั้งที่มีการเปลี่ยนแปลงโค้ดเพียงเล็กน้อย ข้อวิจารณ์นี้รุนแรงเป็นพิเศษสำหรับการทดสอบ UI ซึ่งสามารถเปลี่ยนแปลงได้ง่ายและใช้เวลาบำรุงรักษานาน สำหรับนักพัฒนาเหล่านี้ ต้นทุนในการเขียนและบำรุงรักษาการทดสอบไม่สามารถรับประกันประโยชน์ที่คาดหวังได้ นำไปสู่การตั้งคำถามต่อหนึ่งในหลักการที่ถูกยึดถือมากที่สุดของวิศวกรรมซอฟต์แวร์สมัยใหม่

ข้อโต้แย้งในการอภิปรายเรื่องการทดสอบ

สนับสนุนการทดสอบ:

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

คัดค้านการทดสอบ:

  • ต้นทุนการบำรุงรักษาสูงสำหรับการทดสอบที่เปราะบาง
  • การลงทุนด้านเวลาไม่คุ้มค่ากับผลประโยชน์ที่ได้เสมอไป
  • อาจสร้างความมั่นใจที่ผิดพลาดหากเขียนได้ไม่ดี
  • อาจทดสอบการทำงานภายในแทนที่จะเป็นพฤติกรรม
  • การทดสอบ UI โดยเฉพาะมีความไม่เสถียรและมีค่าใช้จ่ายสูง

การปกป้องการทดสอบในเชิงปฏิบัติ

อีกฝั่งของการอภิปราย นักพัฒนาจำนวนมากได้ยกตัวอย่างที่เป็นรูปธรรมถึงคุณค่าทางปฏิบัติของการทดสอบ นักพัฒนาคนหนึ่งแบ่งปันว่าการทดสอบมีความสำคัญอย่างยิ่งในช่วงการปรับโครงสร้าง SQL query builder โดยสามารถระบุได้ทันทีว่า JOIN clauses ขาด ON conditions ไป การตอบรับแบบทันทีเช่นนี้มีค่าอนันต์ในระบบที่ซับซ้อน อีกคนระบุว่าการเขียนโค้ดโดยคำนึงถึงการทดสอบจะนำไปสู่การออกแบบที่บำรุงรักษาได้ง่ายกว่าและเรียบง่ายกว่า เนื่องจากฟังก์ชันที่ทำหลายสิ่งเกินไปหรือมีผลข้างเคียงมากมายนั้นทดสอบได้ยากอย่างยิ่ง ข้อโต้แย้งที่น่าสนใจที่สุดมาจากผู้ที่ใช้การทดสอบเป็นเครื่องมือในการออกแบบ โดยนักพัฒนาคนหนึ่งระบุว่าประมาณสามถึงห้าครั้งต่อปี การทดสอบช่วยให้ฉันสร้างฟีเจอร์ที่มีประโยชน์มากขึ้น เพราะฉันตระหนักว่ามีกรณีขอบ (corner case) ที่น่าสนใจซึ่งตรวจสอบได้ง่ายและนำไปใช้ได้ง่าย

ฉันปล่อยโค้ดที่ถูกปรับเปลี่ยนให้ซับซ้อนสูงมากเข้าสู่ production โดยตรง และไม่พบข้อบกพร่องใน production เลยมาเกิน 6 ปีแล้ว สาเหตุที่เป็นเช่นนี้ก็เพราะการทดสอบอัตโนมัติ

สเปกตรัมของการทดสอบและช่องว่างทักษะ

การอภิปรายเผยให้เห็นว่าการทดสอบไม่ใช่ทางเลือกแบบสองขั้ว แต่มีอยู่บนสเปกตรัมของคุณภาพและประสิทธิภาพ ดังที่ผู้แสดงความคิดเห็นคนหนึ่งสังเกตอย่างเฉียบคมว่า มีสงครามสามฝ่ายในการทดสอบ ระหว่างคนที่คิดว่ามันแย่และเขียนการทดสอบที่กลายเป็นคำทำนายที่เกิดขึ้นจริงด้วยตัวเอง คนที่คิดว่าการทดสอบที่แย่ไม่ได้แย่และคิดว่าการทดสอบที่ดีคือ 'การทำงานที่มากเกินไป' และคนกลุ่มน้อยที่คิดว่าการทดสอบที่แตกต่างกันก็แค่เป็นงานที่แตกต่างกัน สิ่งนี้ชี้ให้เห็นว่าปัญหาอาจไม่ใช่ตัวการทดสอบเอง แต่เป็นช่องว่างทักษะที่แพร่หลายในการเขียนการทดสอบที่มีคุณค่า นักพัฒนาหลายคนเน้นย้ำว่าการเรียนรู้ที่จะเขียนการทดสอบที่มีประสิทธิภาพจำเป็นต้องมีที่ปรึกษาและการฝึกฝน โดยมีคนหนึ่งแบ่งปันว่าต้องการคำแนะนำจากที่ปรึกษาหลายคนเพื่อให้รู้สึกเชี่ยวชาญ ศิลปะอยู่ที่การสร้างการทดสอบที่ประหยัดและครอบคลุมฟีเจอร์หลักที่สร้างรายได้ให้ธุรกิจ ควรจะหยาบในระดับที่ปฏิบัติได้จริง

ประเภทการทดสอบทั่วไปและวัตถุประสงค์

  • Unit Tests: ตรวจสอบส่วนประกอบหรือฟังก์ชันแต่ละตัวแยกกัน มีคุณค่าอย่างยิ่งสำหรับอัลกอริทึมที่ซับซ้อนและใช้เป็นเอกสารที่สามารถรันได้
  • Integration Tests: ตรวจสอบว่าส่วนประกอบหลายตัวทำงานร่วมกันได้อย่างถูกต้อง จำเป็นสำหรับเวิร์กโฟลว์ที่สำคัญและพฤติกรรมของระบบ
  • UI/E2E Tests: ทดสอบเวิร์กโฟลว์ของผู้ใช้ทั้งหมดผ่านอินเทอร์เฟซ สามารถจับปัญหาเลย์เอาต์ที่เฉพาะเจาะจงกับเบราว์เซอร์หรืออุปกรณ์ได้ แต่มักจะไม่เสถียรและมีค่าใช้จ่ายในการดูแลรักษาสูง
  • Regression Tests: มุ่งเป้าไปที่บั๊กที่แก้ไขไปแล้วโดยเฉพาะเพื่อป้องกันไม่ให้เกิดซ้ำ ได้รับการยอมรับอย่างกว้างขวางว่าให้ผลตอบแทนจากการลงทุนที่ชัดเจนที่สุด
  • Exploratory Testing: การทดสอบด้วยตนเองเพื่อตรวจสอบข้อกำหนดกับความต้องการในโลกแห่งความเป็นจริงและค้นพบพฤติกรรมที่ไม่คาดคิด

ทางเลือกอื่นด้วยการตรวจสอบอย่างเป็นทางการ

นอกจากประเด็นการทดสอบแล้ว นักพัฒนาบางคนชี้ไปที่แนวทางที่เข้มงวดมากขึ้นเพื่อรับประกันความถูกต้องของโค้ด บทความต้นฉบับกล่าวถึง Proof-Driven Development และคุณค่าทางธุรกิจของ clean code ที่ให้เหตุผลได้ง่ายขึ้น ผู้แสดงความคิดเห็นขยายความเรื่องนี้โดยพูดถึงความเป็นไปได้ในทางทฤษฎีของ formal verification — การพิสูจน์ความถูกต้องของโปรแกรมด้วยคณิตศาสตร์ อย่างไรก็ตาม พวกเขาระบุข้อจำกัดในทางปฏิบัติอย่างรวดเร็ว: การพิสูจน์ความถูกต้องจะต้องตรวจสอบไม่เพียงแต่โค้ดของคุณ แต่ยังรวมถึงทุก dependencies, compiler, standard library และแม้แต่ instruction set architecture ของ processor เมื่อพิจารณาจากการค้นพบข้อบกพร่องใน processor หลักเช่นสถาปัตยกรรม Intel RDRAND และ Skylake ในโลกความเป็นจริง แนวทางนี้ยังคงเป็นทฤษฎีสำหรับการประยุกต์ใช้ส่วนใหญ่ แม้ว่ามันจะเน้นย้ำถึงความท้าทายพื้นฐานในการบรรลุความแน่นอนสัมบูรณ์ในระบบที่ซับซ้อน

การหาจุดกึ่งกลาง

นักพัฒนาที่มีประสบการณ์ส่วนใหญ่ในที่สุดก็พบแนวทางที่สมดุลซึ่งทำงานได้ในบริบทเฉพาะของพวกเขา ฉันทามติชี้ให้เห็นว่า regression tests — การทดสอบที่เขียนเพื่อป้องกันไม่ให้ข้อบกพร่องที่เคยแก้ไขแล้วเกิดขึ้นอีก — ให้คุณค่าที่ชัดเจนอย่างสม่ำเสมอ ดังที่นักพัฒนาคนหนึ่งระบุว่า จากประสบการณ์ของผม ในบรรดาการทดสอบทุกประเภทที่สามารถเขียนได้ การมองเห็นว่า regression tests นั้นคุ้มค่ากับการลงทุนเป็นเรื่องที่ง่ายที่สุด กุญแจสำคัญคือกลยุทธ์การทดสอบที่รอบคอบมากกว่าการยึดติดกับระเบียบวิธีใดวิธีหนึ่งอย่างเคร่งครัด การทดสอบประเภทต่างๆ มีจุดประสงค์ที่แตกต่างกัน: unit tests สำหรับอัลกอริทึมที่ซับซ้อน, integration tests สำหรับเวิร์กโฟลว์ที่สำคัญ, และการทดสอบด้วยมือแบบสำรวจ (exploratory testing) อย่างระมัดระวังเพื่อตรวจสอบความถูกต้องของข้อกำหนดกับความต้องการในโลกแห่งความเป็นจริง ทีมที่ประสบความสำเร็จสูงสุดดูเหมือนจะเป็นทีมที่ปฏิบัติต่อการทดสอบเป็นเครื่องมือทางปฏิบัติ แทนที่จะเป็นหลักคำสอน โดยมุ่งเน้นไปที่การทดสอบที่ให้ความมั่นใจอย่างแท้จริงโดยไม่สร้างภาระในการบำรุงรักษา

การอภิปรายเรื่องการทดสอบในท้ายที่สุดสะท้อนให้เห็นถึงความท้าทายที่กว้างขึ้นของวิศวกรรมซอฟต์แวร์: การสร้างสมดุลระหว่างแนวปฏิบัติในอุดมคติกับข้อจำกัดในทางปฏิบัติ แม้ว่าการทดสอบอัตโนมัติจะให้ประโยชน์อย่างมากในการตรวจจับ regression การทำให้สามารถปรับโครงสร้างใหม่ได้ และแม้กระทั่งการปรับปรุงการออกแบบ แต่การทดสอบที่ไม่ดีก็สามารถกลายเป็นภาระที่มีค่าใช้จ่ายสูงได้เช่นกัน แนวทางที่ประสบความสำเร็จมากที่สุดดูเหมือนจะเป็นแนวทางที่เน้นปฏิบัติ — การลงทุนในการทดสอบที่ให้คุณค่าชัดเจน ในขณะที่หลีกเลี่ยงการยึดติดกับการทดสอบเพียงเพื่อการทดสอบ ดังเช่นการตัดสินใจด้านวิศวกรรมส่วนใหญ่ บริบทมีความสำคัญ และกลยุทธ์การทดสอบที่เหมาะสมขึ้นอยู่กับความต้องการเฉพาะของโครงการ ทีม และองค์กร

อ้างอิง: Proof-Driven Development (or- the business value of Clean Code)