คุณรู้ว่าการทำงานกับโค้ดที่ทดสอบไม่ดีนั้นเจ็บปวดเพียงใด ทุกครั้งที่คุณแก้ไขข้อผิดพลาด คุณจะสร้างเพิ่มอีกห้ารายการ และเมื่อสิ่งที่ทำ ทำงาน คุณไม่มีทางรู้จริงๆ ว่ามันถูกออกแบบมาอย่างนั้นหรือแค่ทำงานโดยบังเอิญ
ในทางกลับกัน คุณเพิ่งเขียนสิ่งที่ดูเหมือน 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 สถานการณ์:
- ผู้ใช้เป็นผู้ดูแลระบบ
preferred_notification_method
เป็นอีเมล - ผู้ใช้เป็นผู้ดูแลระบบ
preferred_notification_method
เป็นข้อความ - ผู้ใช้เป็นผู้ดูแลระบบ
preferred_notification_method
ไม่เป็น - ผู้ใช้คือผู้ใช้
preferred_notification_method
เป็นอีเมล - ผู้ใช้คือผู้ใช้
preferred_notification_method
เป็นข้อความ - ผู้ใช้คือผู้ใช้
preferred_notification_method
ไม่เป็น - ผู้ใช้เป็นผู้แต่ง
preferred_notification_method
เป็นอีเมล - ผู้ใช้เป็นผู้แต่ง
preferred_notification_method
เป็นข้อความ - ผู้ใช้เป็นผู้แต่ง
preferred_notification_method
ไม่เป็น - ผู้ใช้ไม่ระบุชื่อ
preferred_notification_method
เป็นอีเมล - ผู้ใช้ไม่ระบุชื่อ
preferred_notification_method
เป็นข้อความ - ผู้ใช้ไม่ระบุชื่อ
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 สถานะที่จะทดสอบ:
- ผู้ใช้เป็นผู้ดูแลระบบ
- ผู้ใช้คือผู้ใช้
- ผู้ใช้เป็นผู้แต่ง
- ผู้ใช้ไม่ระบุชื่อ
preferred_notification_method
เป็นอีเมลpreferred_notification_method
เป็นข้อความpreferred_notification_method
ไม่เป็น- และอีกหนึ่งการทดสอบสำหรับเมธอดหลัก
ที่นี่ คุณบันทึกการทดสอบสี่รายการโดยแยกรหัสออกจากกัน เพื่อให้คุณสามารถทดสอบแยกกันได้ ในตัวอย่างที่สอง เห็นได้ชัดว่าคุณสามารถทำการทดสอบได้น้อยลง และคุณสามารถจินตนาการได้ว่าเมื่อคุณเพิ่มสถานะต่างๆ มากขึ้น การแยกโค้ดของคุณออกเป็นแนวคิดที่ดียิ่งขึ้นได้อย่างไร
ต้องใช้เวลาและการฝึกฝนก่อนที่คุณจะพบสมดุลที่ดีที่สุดระหว่างความโดดเดี่ยวและความสามารถในการอ่าน แต่หากคุณทำลายการพึ่งพาในที่ที่ถูกต้อง คุณก็สามารถทำการทดสอบน้อยลงได้มาก
โฟกัส
แอปของคุณควรได้รับการทดสอบอย่างดี แต่นั่นไม่ได้หมายความว่าทุกส่วนของแอปควรได้รับความสนใจเท่ากันในการทดสอบ
แม้ว่าคุณจะตั้งเป้าให้ครอบคลุมการทดสอบ 100% คุณจะยังไม่ทดสอบทุกอย่าง คุณอาจจะไม่ได้ทดสอบข้อความทุกบรรทัดในมุมมองของคุณ เป็นต้น หรือว่าคุณสำรวจข้อมูลอัปเดตทุก ๆ ห้าวินาทีแทนที่จะเป็นสิบเท่า
นั่นคือจุดที่เน้น เขียนการทดสอบน้อยลงและมีประโยชน์มากขึ้น และตัดสินใจอย่างมีสติซึ่งคุณสามารถใช้เวลาให้คุ้มค่าที่สุด
โฟกัสเป็นอีกสิ่งหนึ่งที่ยากที่จะทำให้ถูกต้อง นี่เป็นคำถามสองสามข้อที่ฉันถามตัวเองเพื่อช่วยให้ฉันมีสมาธิกับการทดสอบที่สำคัญที่สุด:
-
สิ่งนี้เชื่อมโยงถึงกันอย่างไรกับแอปที่เหลือของฉัน ถ้ามันพังจะพังอีกกี่ชิ้น
-
เป็นไปได้มากน้อยเพียงใดที่สิ่งนี้จะเปลี่ยนแปลงโดยธรรมชาติ? หากการทดสอบของฉันล้มเหลว อาจเป็นเพราะข้อบกพร่องหรือเพราะมีคนอัปเดตข้อความใน UI หรือไม่
-
อะไรคือผลกระทบของการทำลายนี้? ฉันจะเรียกเก็บเงินจากบัตรเครดิตของผู้อื่นเป็นครั้งที่สอง หรือเพียงแค่จะมีข้อความหายไป
-
ส่วนนี้ใช้บ่อยแค่ไหน? มีความสำคัญต่อพฤติกรรมของแอปหรือเป็นหน้าเกี่ยวกับที่ฝังอยู่ที่ไหนสักแห่งในส่วนท้ายหรือไม่
คุณไม่ควร เท่านั้น ทดสอบชิ้นส่วนที่สำคัญ แต่คุณจะมีแอปที่รู้สึก คุณภาพที่สูงขึ้นหากคุณใช้เวลาในการทดสอบเป็นอย่างดี
หากคุณพยายามทดสอบทุกเส้นทางที่เป็นไปได้ที่มีผู้ดำเนินการผ่านแอปของคุณ คุณจะไม่มีวันจัดส่ง TDD ช่วยได้ แต่ไม่สามารถแก้ปัญหาการทดสอบทั้งหมดของคุณได้
แน่นอนว่านั่นไม่ได้หมายความว่าคุณไม่ควรทดสอบเลย
คุณสามารถใช้ปิรามิดทดสอบเพื่อให้การทดสอบของคุณมีขนาดเล็ก คุณสามารถแยกและทำลายการพึ่งพาเพื่อเปลี่ยน m * n
กรณีทดสอบเป็น m + n
. และคุณจัดลำดับความสำคัญได้ คุณจึงใช้เวลาทดสอบส่วนที่สำคัญที่สุดของแอปได้มากขึ้น
คุณเท่าไหร่แล้ว ทดสอบ? คุณพิจารณาแนวคิดเหล่านี้ขณะสร้างแอปของคุณหรือไม่ และคุณทราบได้อย่างไรว่าส่วนใดของแอปจะเน้นไปที่ส่วนใด แสดงความคิดเห็นและบอกฉันเกี่ยวกับเรื่องนี้!