แยกหน่วยทดสอบตามความต้องการหรือวิธีการ


16

ก่อนอื่นขอโทษสำหรับชื่อฉันไม่สามารถคิดถึงวิธีที่ง่ายที่สุดในการอธิบาย!

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

public void HandleItem(item a)
{         
     CreateNewItem();
     UpdateStatusOnPreviousItem();
     SetNextRunDate();
}

ดังนั้นคลาสนี้มีวิธีพับลิกหนึ่งที่เรียกใช้เมธอดส่วนตัวเพื่อทำตรรกะ

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

public void GivenItem_WhenRun_Thenxxxxx
{
     HandleItem(item);
     // Assert item has been created
     // Assert status has been set on the previous item
     // Assert run date has been set
}

แต่ฉันคิดว่าฉันสามารถเขียนเป็นแบบทดสอบแยกกันสามแบบ:

public void GivenItem_WhenRun_ThenItemIsCreated()
{
    HandleItem(item);
}

public void GivenItem_WhenRun_ThenStatusIsUpdatedOnPreviousItem()
{
   HandleItem(item);
}

public void GivenItem_WhenRun_ThenRunDateIsSet()
{
     HandleItem(item);
}

ดังนั้นสำหรับฉันนี่ดูเหมือนจะดีกว่าเนื่องจากมันเป็นข้อกำหนดในการแสดงรายการ แต่ทั้งสามนั้นเกี่ยวข้องกันและต้องการงานเดียวกันที่ดำเนินการกับวิธีการทดสอบอย่างแน่นอนดังนั้นฉันจึงใช้งานรหัสเดียวกัน 3 ครั้ง

มีวิธีแนะนำให้ทำอย่างไรกับเรื่องนี้?

ขอบคุณ

คำตอบ:


29

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

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

  • ถ้าหากความล้มเหลวของการทดสอบครั้งแรกมักจะบอกเป็นนัยว่าการทดสอบอีกสองครั้งก็จะล้มเหลวเช่นกันดังนั้นจึงเป็นการดีกว่าที่จะใช้วิธีการแรก (เนื่องจากมันไม่เหมาะสมที่จะทำการทดสอบหากคุณรู้ล่วงหน้ามาก่อน ล้มเหลว).


2
+1 จุดดี มันไม่ได้เกิดขึ้นกับฉันที่เวลาสร้างอาจเป็นคอขวด
Kilian Foth

1
@KilianFoth: คุณไม่ได้ทำงานใน C ++ บ่อยครั้งพอ :(
Matthieu M.

1
@ MatthieuM .: เพื่อความยุติธรรมคำถามจะถูกแท็ก "C #"
Doc Brown เมื่อ

10

คำตอบสั้น ๆ : มันเป็นสิ่งสำคัญมากที่การทดสอบของคุณครอบคลุมทุกฟังก์ชันการทำงานกว่าวิธีการที่พวกเขาทำมัน

คำตอบที่ยาวกว่า: หากคุณยังต้องการเลือกวิธีแก้ปัญหาที่เทียบเท่ากันเหล่านี้คุณสามารถใช้เกณฑ์เสริมสำหรับสิ่งที่ดีที่สุด ตัวอย่างเช่น

  • ความสามารถในการอ่าน: หากวิธีการทำสิ่งต่างๆมากมายที่ไม่เกี่ยวข้องอย่างใกล้ชิดการทดสอบแบบรวมอาจยากที่จะเข้าใจ อย่างไรก็ตามวิธีการนั้นอาจยากที่จะเข้าใจดังนั้นบางทีคุณควรปรับวิธีการใหม่มากกว่าการทดสอบ!)
  • ประสิทธิภาพ: หากการดำเนินการวิธีใช้เวลานานนั่นอาจเป็นเหตุผลที่อ่อนแอสำหรับการรวมการตรวจสอบทั้งสามเพื่อประหยัดเวลา
  • ประสิทธิภาพ 2: หากการรันรหัสการติดตั้งในเฟรมเวิร์กของคุณใช้เวลานานนั่นอาจเป็นเหตุผลที่อ่อนแอในการหลีกเลี่ยงวิธีการทดสอบหลายวิธี (อย่างไรก็ตามหากเป็นปัญหาจริง ๆ คุณอาจควรแก้ไขหรือเปลี่ยนแปลงการตั้งค่าการทดสอบของคุณ - การทดสอบการถดถอยจะสูญเสียคุณค่าไปมากหากคุณไม่สามารถเรียกใช้พวกมันได้อย่างรวดเร็ว)

2

ใช้การเรียกใช้วิธีเดียวด้วยการยืนยันหลายครั้ง นี่คือเหตุผล:

เมื่อคุณทดสอบ HandleItem (a) คุณกำลังทดสอบว่าวิธีการได้นำสินค้าเข้าสู่สถานะที่ถูกต้อง แทนที่จะเป็น "หนึ่งยืนยันต่อการทดสอบ" คิดว่า "หนึ่งแนวคิดตรรกะต่อการทดสอบ"

คำถาม: ถ้า CreateNewItem ล้มเหลว แต่อีกสองวิธีประสบความสำเร็จนี่หมายความว่า HandleItem ดำเนินการสำเร็จหรือไม่ ฉันคาดเดาไม่ได้

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

IMO คำถามเหล่านี้มักเป็นสัญญาณของสิ่งอื่น เป็นสัญญาณว่า HandleItem ไม่ใช่สิ่งที่คุณสามารถ "ทดสอบหน่วย" เพราะดูเหมือนว่าจะมอบหมายให้วิธีการอื่น เมื่อคุณเพียงแค่ตรวจสอบว่า HandleItem เรียกใช้วิธีการอื่นอย่างถูกต้องมันจะกลายเป็นผู้สมัครทดสอบการรวมระบบมากขึ้น (ซึ่งในกรณีนี้คุณยังคงมี 3 ชุด)

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


0

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


-1

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


-2

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.