Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เท่าไหร่การทดสอบมากเกินไป?

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

ในทางกลับกัน คุณเพิ่งเขียนสิ่งที่ดูเหมือน 200 การทดสอบเพื่อส่งคุณลักษณะเล็กๆ หนึ่งรายการ คุณต้องออกแบบโค้ดที่ทำงานอยู่แล้วใหม่อย่างต่อเนื่องเพื่อให้ครอบคลุมการทดสอบ 100% คุณไม่สามารถสั่นคลอนความรู้สึกว่าโค้ดที่ทดสอบดีที่สุดของคุณกำลัง น้อยลง อ่านได้ และที่แย่ที่สุดคือ คุณเริ่มหมดหนทางในแอปของคุณ

ต้องมีพื้นกลาง แล้วค่าสอบเท่าไหร่ถึงจะเหมาะสม?

คงจะดีถ้ามีตัวเลขกลมๆ ดีๆ ที่คุณสามารถใช้ได้เป็นกฎ:โค้ดทดสอบเป็นสองเท่าของโค้ดแอป หรืออาจครอบคลุมการทดสอบ 95% แต่การพูดว่า "ครอบคลุมการทดสอบ 95%" ก็ยังคลุมเครือ

ความครอบคลุมสามารถเป็นตัวบ่งชี้ ของรหัสที่ผ่านการทดสอบอย่างดี แต่ไม่ใช่ การรับประกัน ของรหัสที่ผ่านการทดสอบอย่างดี ฉันมีแอปที่ครอบคลุม 100% ซึ่งมีข้อบกพร่องมากกว่าแอปที่มีความครอบคลุม 85%

ดังนั้น การทดสอบในปริมาณที่เหมาะสมต้องไม่เกี่ยวกับตัวเลข แต่เป็นเรื่องเกี่ยวกับบางสิ่งที่คลุมเครือและยากที่จะกำหนด เป็นการทดสอบ อย่างมีประสิทธิภาพ .

การทดสอบอย่างมีประสิทธิภาพ

การทดสอบอย่างมีประสิทธิภาพคือทั้งหมดที่เกี่ยวกับการได้รับประโยชน์สูงสุดสำหรับปริมาณงานที่น้อยที่สุด ฟังดูดีมากใช่ไหม

แต่มีหลายอย่างที่ทำการทดสอบได้อย่างมีประสิทธิภาพมากขึ้น โดยเฉพาะอย่างยิ่งการคิดถึงสามสิ่ง:ขนาด การแยกตัว และโฟกัส

ขนาด

การทดสอบการบูรณาการนั้นยอดเยี่ยม พวกเขาสะท้อนเส้นทางที่บุคคลจริงใช้ผ่านแอปของคุณ พวกเขาทดสอบโค้ดทั้งหมดของคุณ โดยทำงานร่วมกันในลักษณะเดียวกับที่ใช้ในโลกแห่งความเป็นจริง

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

การทดสอบหน่วยมีขนาดเล็กลง พวกเขาวิ่งเร็วขึ้น ง่ายต่อการคิด เนื่องจากคุณต้องเก็บส่วนเล็ก ๆ ของระบบไว้ในหัวขณะเขียน

แต่ก็สามารถปลอมได้เช่นกัน เพียงเพราะบางสิ่งใช้งานได้ในการทดสอบหน่วยไม่ได้หมายความว่ามันจะใช้งานได้ในโลกแห่งความเป็นจริงด้วย (โดยเฉพาะถ้าคุณกำลังเยาะเย้ยมาก)

แล้วคุณจะสร้างสมดุลได้อย่างไร

เนื่องจากการทดสอบหน่วยนั้นรวดเร็วและเขียนง่าย จึงไม่เสียค่าใช้จ่ายมากนักที่จะมีจำนวนมาก จึงเป็นที่ที่ดีในการทดสอบสิ่งต่างๆ เช่น Edge case และตรรกะที่ซับซ้อน

เมื่อคุณมีชิ้นส่วนของระบบที่ผ่านการทดสอบมาเป็นอย่างดีแล้ว คุณยังต้องกรอกข้อมูลในช่องว่างต่างๆ คุณต้องทดสอบว่าส่วนต่างๆ เหล่านั้นโต้ตอบกันอย่างไร และการเดินทางเต็มรูปแบบที่ใครบางคนสามารถใช้ผ่านแอปของคุณได้ แต่เนื่องจากกรณีและตรรกะของ Edge ส่วนใหญ่ได้รับการทดสอบโดย Unit Test คุณจึงต้องใช้การทดสอบการรวมที่ซับซ้อนและช้ากว่านี้เพียงไม่กี่รายการ

คุณจะได้ยินแนวคิดนี้ที่เรียกว่า “Test Pyramid” เป็นการทดสอบการรวมระบบบางส่วน ซึ่งอยู่บนฐานของการทดสอบหลายหน่วย และถ้าคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้ ลองดูบทที่สามของหนังสือ Practicing Rails ของฉัน

การแยกตัว

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

สมมติว่าคุณมีวัตถุที่อาจอยู่ในสถานะที่แตกต่างกันสองสามสถานะ:

case user.type
when :admin
  message = admin_message
when :user
  message = user_message
when :author
  message = author_message
else
  message = anonymous_message
end

if user.preferred_notification_method = :email
  send_email(message)
elsif user.preferred_notification_method = :text
  send_text_message(message)
else
  queue_notification(message) 
end

หากคุณต้องการทดสอบทุกเส้นทางที่เป็นไปได้ที่นี่ คุณจะต้องทดสอบ 12 สถานการณ์:

  1. ผู้ใช้เป็นผู้ดูแลระบบ preferred_notification_method เป็นอีเมล
  2. ผู้ใช้เป็นผู้ดูแลระบบ preferred_notification_method เป็นข้อความ
  3. ผู้ใช้เป็นผู้ดูแลระบบ preferred_notification_method ไม่เป็น
  4. ผู้ใช้คือผู้ใช้ preferred_notification_method เป็นอีเมล
  5. ผู้ใช้คือผู้ใช้ preferred_notification_method เป็นข้อความ
  6. ผู้ใช้คือผู้ใช้ preferred_notification_method ไม่เป็น
  7. ผู้ใช้เป็นผู้แต่ง preferred_notification_method เป็นอีเมล
  8. ผู้ใช้เป็นผู้แต่ง preferred_notification_method เป็นข้อความ
  9. ผู้ใช้เป็นผู้แต่ง preferred_notification_method ไม่เป็น
  10. ผู้ใช้ไม่ระบุชื่อ preferred_notification_method เป็นอีเมล
  11. ผู้ใช้ไม่ระบุชื่อ preferred_notification_method เป็นข้อความ
  12. ผู้ใช้ไม่ระบุชื่อ preferred_notification_method ไม่เป็น

มีหลายกรณีที่ต้องทดสอบ เนื่องจาก "การส่งข้อความตามวิธีการแจ้งเตือน" และ "การสร้างข้อความตามประเภทผู้ใช้" นั้นเชื่อมโยงกัน คุณ อาจ สามารถบีบได้น้อยลง แต่ก็ไม่ชัดเจน – และเป็นเพียงการขอข้อบกพร่อง

แต่ถ้าคุณแยกพวกมันออกจากกันล่ะ

message = get_message_based_on_user_type(user.type)

send_notification(message, user.preferred_notification_method)

ตอนนี้คุณสามารถทดสอบแต่ละส่วนแยกกันได้

สำหรับส่วนแรก คุณสามารถทดสอบว่าข้อความที่ถูกต้องถูกส่งกลับสำหรับผู้ใช้แต่ละประเภท

สำหรับส่วนที่สอง คุณสามารถทดสอบว่าข้อความที่ระบุถูกส่งอย่างถูกต้องตามค่าของ preferred_notification_method .

และสุดท้าย คุณสามารถทดสอบว่าวิธีการหลักจะส่งข้อความที่ส่งคืนจาก do_stuff_based_on_user_type พร้อมกับ send_email_or_text . ตอนนี้คุณมี 8 สถานะที่จะทดสอบ:

  1. ผู้ใช้เป็นผู้ดูแลระบบ
  2. ผู้ใช้คือผู้ใช้
  3. ผู้ใช้เป็นผู้แต่ง
  4. ผู้ใช้ไม่ระบุชื่อ
  5. preferred_notification_method เป็นอีเมล
  6. preferred_notification_method เป็นข้อความ
  7. preferred_notification_method ไม่เป็น
  8. และอีกหนึ่งการทดสอบสำหรับเมธอดหลัก

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

ต้องใช้เวลาและการฝึกฝนก่อนที่คุณจะพบสมดุลที่ดีที่สุดระหว่างความโดดเดี่ยวและความสามารถในการอ่าน แต่หากคุณทำลายการพึ่งพาในที่ที่ถูกต้อง คุณก็สามารถทำการทดสอบน้อยลงได้มาก

โฟกัส

แอปของคุณควรได้รับการทดสอบอย่างดี แต่นั่นไม่ได้หมายความว่าทุกส่วนของแอปควรได้รับความสนใจเท่ากันในการทดสอบ

แม้ว่าคุณจะตั้งเป้าให้ครอบคลุมการทดสอบ 100% คุณจะยังไม่ทดสอบทุกอย่าง คุณอาจจะไม่ได้ทดสอบข้อความทุกบรรทัดในมุมมองของคุณ เป็นต้น หรือว่าคุณสำรวจข้อมูลอัปเดตทุก ๆ ห้าวินาทีแทนที่จะเป็นสิบเท่า

นั่นคือจุดที่เน้น เขียนการทดสอบน้อยลงและมีประโยชน์มากขึ้น และตัดสินใจอย่างมีสติซึ่งคุณสามารถใช้เวลาให้คุ้มค่าที่สุด

โฟกัสเป็นอีกสิ่งหนึ่งที่ยากที่จะทำให้ถูกต้อง นี่เป็นคำถามสองสามข้อที่ฉันถามตัวเองเพื่อช่วยให้ฉันมีสมาธิกับการทดสอบที่สำคัญที่สุด:

  • สิ่งนี้เชื่อมโยงถึงกันอย่างไรกับแอปที่เหลือของฉัน ถ้ามันพังจะพังอีกกี่ชิ้น

  • เป็นไปได้มากน้อยเพียงใดที่สิ่งนี้จะเปลี่ยนแปลงโดยธรรมชาติ? หากการทดสอบของฉันล้มเหลว อาจเป็นเพราะข้อบกพร่องหรือเพราะมีคนอัปเดตข้อความใน UI หรือไม่

  • อะไรคือผลกระทบของการทำลายนี้? ฉันจะเรียกเก็บเงินจากบัตรเครดิตของผู้อื่นเป็นครั้งที่สอง หรือเพียงแค่จะมีข้อความหายไป

  • ส่วนนี้ใช้บ่อยแค่ไหน? มีความสำคัญต่อพฤติกรรมของแอปหรือเป็นหน้าเกี่ยวกับที่ฝังอยู่ที่ไหนสักแห่งในส่วนท้ายหรือไม่

คุณไม่ควร เท่านั้น ทดสอบชิ้นส่วนที่สำคัญ แต่คุณจะมีแอปที่รู้สึก คุณภาพที่สูงขึ้นหากคุณใช้เวลาในการทดสอบเป็นอย่างดี

หากคุณพยายามทดสอบทุกเส้นทางที่เป็นไปได้ที่มีผู้ดำเนินการผ่านแอปของคุณ คุณจะไม่มีวันจัดส่ง TDD ช่วยได้ แต่ไม่สามารถแก้ปัญหาการทดสอบทั้งหมดของคุณได้

แน่นอนว่านั่นไม่ได้หมายความว่าคุณไม่ควรทดสอบเลย

คุณสามารถใช้ปิรามิดทดสอบเพื่อให้การทดสอบของคุณมีขนาดเล็ก คุณสามารถแยกและทำลายการพึ่งพาเพื่อเปลี่ยน m * n กรณีทดสอบเป็น m + n . และคุณจัดลำดับความสำคัญได้ คุณจึงใช้เวลาทดสอบส่วนที่สำคัญที่สุดของแอปได้มากขึ้น

คุณเท่าไหร่แล้ว ทดสอบ? คุณพิจารณาแนวคิดเหล่านี้ขณะสร้างแอปของคุณหรือไม่ และคุณทราบได้อย่างไรว่าส่วนใดของแอปจะเน้นไปที่ส่วนใด แสดงความคิดเห็นและบอกฉันเกี่ยวกับเรื่องนี้!