การทดสอบหน่วยที่ดีคืออะไร? [ปิด]


97

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

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

ขอแนะนำให้ใช้ภาษาที่ไม่เชื่อเรื่องพระเจ้า

คำตอบ:


93

ให้ฉันเริ่มต้นด้วยการเสียบซอร์ส - Pragmatic Unit Testing ใน Java กับ JUnit (มีรุ่นที่มี C # -Nunit ด้วย .. แต่ฉันมีอันนี้ .. มันไม่เชื่อเรื่องพระเจ้าเป็นส่วนใหญ่แนะนำ)

การทดสอบที่ดีควรเป็นTRIP (ตัวย่อไม่เหนียวพอ - ฉันมีพิมพ์ cheatsheet ในหนังสือที่ฉันต้องดึงออกมาเพื่อให้แน่ใจว่าฉันได้สิ่งนี้ถูกต้อง .. )

  • อัตโนมัติ : การเรียกใช้การทดสอบรวมทั้งการตรวจสอบผลสำหรับ PASS / FAIL ควรเป็นไปโดยอัตโนมัติ
  • อย่างละเอียด : ความครอบคลุม; แม้ว่าจุดบกพร่องมักจะรวมกลุ่มในบางภูมิภาคในโค้ด แต่ให้แน่ใจว่าคุณได้ทดสอบพา ธ และสถานการณ์สำคัญทั้งหมด .. ใช้เครื่องมือหากคุณต้องรู้จักพื้นที่ที่ยังไม่ทดสอบ
  • ทำซ้ำได้ : การทดสอบควรให้ผลลัพธ์เหมือนกันทุกครั้ง.. ทุกครั้ง การทดสอบไม่ควรอาศัยพารามิเตอร์ที่ควบคุมไม่ได้
  • อิสระ : สำคัญมาก
    • การทดสอบควรทดสอบเพียงสิ่งเดียวในเวลา การยืนยันหลายครั้งก็ใช้ได้ตราบเท่าที่พวกเขาทั้งหมดทดสอบคุณลักษณะ / พฤติกรรมเดียว เมื่อการทดสอบล้มเหลวควรระบุตำแหน่งของปัญหา
    • การทดสอบไม่ควรพึ่งพาซึ่งกันและกัน - แยก ไม่มีสมมติฐานเกี่ยวกับลำดับของการดำเนินการทดสอบ ตรวจสอบให้แน่ใจว่า 'clean slate' ก่อนการทดสอบแต่ละครั้งโดยใช้การตั้งค่า / การฉีกขาดอย่างเหมาะสม
  • ผู้เชี่ยวชาญ : ในระยะยาวคุณจะมีรหัสทดสอบมากพอ ๆ กับการผลิต (ถ้าไม่มากกว่านั้น) ดังนั้นจึงเป็นไปตามมาตรฐานเดียวกันของการออกแบบที่ดีสำหรับรหัสทดสอบของคุณ คลาสวิธีการแยกตัวประกอบที่ดีพร้อมชื่อที่เปิดเผยโดยเจตนาไม่มีการทำซ้ำการทดสอบด้วยชื่อที่ดี ฯลฯ

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

ปรับปรุง 2010-08:

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

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


เราอาจไม่เห็นด้วยกับการใช้ Mocks แต่นี่เป็นการเขียนแนวทางปฏิบัติที่ดีที่สุดในการทดสอบหน่วย
Justin Standard

ฉันจะหาคำตอบนี้เป็นคำตอบเพราะฉันคิดว่าคำย่อ "A TRIP" มีประโยชน์
Spoike

3
ส่วนใหญ่ฉันเห็นด้วย แต่อยากจะชี้ให้เห็นว่าการทดสอบโค้ดที่คุณไม่ได้เป็นเจ้าของมีประโยชน์ ... คุณกำลังทดสอบว่าเป็นไปตามข้อกำหนดของคุณ คุณจะมั่นใจได้อย่างไรว่าการอัปเกรดจะไม่ทำให้ระบบของคุณเสียหาย (แต่แน่นอนว่าให้คำนึงถึงอัตราส่วนต้นทุน / ผลประโยชน์เมื่อทำเช่นนั้น)
ท้อแท้

@ Craig - ฉันเชื่อว่าคุณกำลังอ้างถึงการทดสอบการถดถอย (ระดับอินเทอร์เฟซ) (หรือการทดสอบผู้เรียนในบางกรณี) ซึ่งพฤติกรรมของเอกสารที่คุณขึ้นอยู่ ฉันจะไม่เขียนการทดสอบ 'หน่วย' สำหรับรหัสของบุคคลที่สามเนื่องจากก. ผู้ขายรู้ข้อมูลเกี่ยวกับรหัสนั้นมากกว่าฉันข. ผู้ขายไม่ผูกพันที่จะรักษาการใช้งานเฉพาะใด ๆ ฉันไม่ได้ควบคุมการเปลี่ยนแปลงใน codebase นั้นและฉันไม่ต้องการใช้เวลาแก้ไขการทดสอบที่เสียด้วยการอัปเกรด ดังนั้นฉันควรเขียนโค้ดการทดสอบการถดถอยระดับสูงสำหรับพฤติกรรมที่ฉันใช้ (และต้องการได้รับการแจ้งเตือนเมื่อเสีย)
Gishu

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

42
  1. อย่าเขียนการทดสอบที่ยิ่งใหญ่ ตามที่ "หน่วย" ใน "การทดสอบหน่วย" แนะนำให้ทำให้แต่ละหน่วยเป็นอะตอมและแยกออกจากกันให้มากที่สุด หากคุณต้องสร้างเงื่อนไขเบื้องต้นโดยใช้อ็อบเจ็กต์จำลองแทนที่จะสร้างสภาพแวดล้อมผู้ใช้ทั่วไปด้วยตนเองมากเกินไป
  2. อย่าทดสอบสิ่งที่เห็นได้ชัดว่าได้ผล หลีกเลี่ยงการทดสอบคลาสจากผู้ขายบุคคลที่สามโดยเฉพาะอย่างยิ่งผู้ที่จัดหา API หลักของเฟรมเวิร์กที่คุณเขียนโค้ดเช่นอย่าทดสอบการเพิ่มไอเท็มในคลาส Hashtable ของผู้ขาย
  3. พิจารณาใช้เครื่องมือครอบคลุมรหัสเช่น NCover เพื่อช่วยค้นหาขอบกรณีที่คุณยังไม่ได้ทดสอบ
  4. ลองเขียนแบบทดสอบก่อนการใช้งาน คิดว่าการทดสอบเป็นข้อกำหนดเพิ่มเติมที่การใช้งานของคุณจะเป็นไปตาม Cf. นอกจากนี้การพัฒนาที่ขับเคลื่อนด้วยพฤติกรรมซึ่งเป็นสาขาเฉพาะของการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ
  5. คงเส้นคงวา. หากคุณเขียนเฉพาะการทดสอบสำหรับโค้ดบางส่วนก็แทบไม่มีประโยชน์ หากคุณทำงานเป็นทีมและคนอื่น ๆ บางส่วนหรือทั้งหมดไม่เขียนแบบทดสอบก็ไม่มีประโยชน์เช่นกัน โน้มน้าวตัวเองและคนอื่น ๆ ถึงความสำคัญ (และคุณสมบัติที่ช่วยประหยัดเวลา ) ของการทดสอบหรืออย่ากังวล

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

1
ฉันเห็นด้วย. อย่างไรก็ตามในระยะยาวคุณต้องสามารถพึ่งพาการทดสอบที่อยู่ที่นั่นกล่าวคือสามารถสันนิษฐานได้ว่าข้อผิดพลาดทั่วไปจะถูกจับโดยพวกเขา มิฉะนั้นผลประโยชน์จะลดลงอย่างมาก
Sören Kuklau

2
"หากคุณเขียนการทดสอบสำหรับโค้ดบางส่วนเท่านั้นก็แทบไม่มีประโยชน์เลย" เป็นแบบนี้จริงๆเหรอ? ฉันมีโครงการที่มีรหัสครอบคลุม 20% (พื้นที่สำคัญ / มีแนวโน้มที่จะล้มเหลว) และพวกเขาช่วยฉันอย่างมากและโครงการก็ใช้ได้เช่นกัน
ดร. ชั่วร้าย

1
ฉันเห็นด้วยกับ Slough แม้ว่าจะมีการทดสอบเพียงเล็กน้อย แต่เนื่องจากมีการเขียนที่ดีและแยกได้มากพอก็จะช่วยได้มาก
Spoike

41

คำตอบส่วนใหญ่ในที่นี้ดูเหมือนจะกล่าวถึงแนวทางปฏิบัติที่ดีที่สุดในการทดสอบหน่วยโดยทั่วไป (เมื่อใดที่ไหนทำไมและอะไร) แทนที่จะเขียนแบบทดสอบด้วยตนเอง (อย่างไร) เนื่องจากคำถามดูค่อนข้างเฉพาะเจาะจงในส่วน "อย่างไร" ฉันจึงคิดว่าจะโพสต์สิ่งนี้โดยนำมาจากการนำเสนอ "กระเป๋าสีน้ำตาล" ที่ฉันจัดทำที่ บริษัท ของฉัน

การทดสอบกฎการเขียน 5 ข้อของ Womp:


1. ใช้ชื่อวิธีการทดสอบแบบพรรณนาแบบยาว ๆ

   - Map_DefaultConstructorShouldCreateEmptyGisMap()
   - ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
   - Dog_Object_Should_Eat_Homework_Object_When_Hungry()

2. เขียนการทดสอบของคุณในรูปแบบสไตล์ / พระราชบัญญัติ / ยืนยัน

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

3. ให้ข้อความแสดงความล้มเหลวพร้อมกับ Asserts ของคุณเสมอ

Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element 
processing events was raised by the XElementSerializer");
  • การฝึกฝนที่เรียบง่าย แต่คุ้มค่าที่ทำให้เห็นได้ชัดในการสมัครนักวิ่งของคุณว่าอะไรล้มเหลว หากคุณไม่ได้ระบุข้อความคุณมักจะได้รับข้อความ "คาดว่าจริงเป็นเท็จ" ในผลลัพธ์ความล้มเหลวซึ่งทำให้คุณต้องไปอ่านการทดสอบเพื่อดูว่ามีอะไรผิดปกติ

4. แสดงความคิดเห็นเหตุผลของการทดสอบ - สมมติฐานทางธุรกิจคืออะไร?

  /// A layer cannot be constructed with a null gisLayer, as every function 
  /// in the Layer class assumes that a valid gisLayer is present.
  [Test]
  public void ShouldNotAllowConstructionWithANullGisLayer()
  {
  }
  • สิ่งนี้อาจดูเหมือนชัดเจน แต่การปฏิบัตินี้จะปกป้องความสมบูรณ์ของการทดสอบของคุณจากผู้ที่ไม่เข้าใจเหตุผลเบื้องหลังการทดสอบตั้งแต่แรก ฉันเคยเห็นการทดสอบจำนวนมากถูกลบหรือแก้ไขซึ่งทำได้ดีมากเพียงเพราะบุคคลนั้นไม่เข้าใจสมมติฐานที่การทดสอบกำลังตรวจสอบ
  • หากการทดสอบไม่สำคัญหรือชื่อวิธีการอธิบายเพียงพอก็อนุญาตให้ปิดความคิดเห็นได้

5. การทดสอบทุกครั้งจะต้องเปลี่ยนสถานะของทรัพยากรที่สัมผัสเสมอ

  • ใช้การล้อเลียนหากเป็นไปได้เพื่อหลีกเลี่ยงการจัดการกับทรัพยากรจริง
  • การล้างข้อมูลต้องทำในระดับทดสอบ การทดสอบจะต้องไม่ขึ้นอยู่กับลำดับการดำเนินการใด ๆ

2
+1 เนื่องจากจุด 1, 2 และ 5 มีความสำคัญ 3 และ 4 ดูเหมือนจะมากเกินไปสำหรับการทดสอบหน่วยหากคุณใช้ชื่อวิธีการทดสอบเชิงพรรณนาอยู่แล้ว แต่ฉันขอแนะนำเอกสารของการทดสอบหากขอบเขตมีขนาดใหญ่ (การทดสอบการทำงานหรือการยอมรับ)
Spoike

+1 สำหรับความรู้และตัวอย่างแบบลงสู่พื้นดินและเชิงปฏิบัติ
Phil

17

คำนึงถึงเป้าหมายเหล่านี้ (ดัดแปลงจากหนังสือ xUnit Test Patterns โดย Meszaros)

  • การทดสอบควรลดความเสี่ยงไม่ใช่แนะนำ
  • การทดสอบควรใช้งานง่าย
  • การทดสอบควรดูแลรักษาได้ง่ายเนื่องจากระบบมีวิวัฒนาการรอบตัว

บางสิ่งที่จะทำให้ง่ายขึ้น:

  • การทดสอบควรล้มเหลวเนื่องจากสาเหตุเดียวเท่านั้น
  • การทดสอบควรทดสอบเพียงสิ่งเดียว
  • ลดการอ้างอิงการทดสอบ (ไม่มีการพึ่งพาฐานข้อมูลไฟล์ ui ฯลฯ )

อย่าลืมว่าคุณสามารถทำการทดสอบแบบผสมผสานกับเฟรมเวิร์ก xUnit ของคุณได้เช่นกันแต่แยกการทดสอบระหว่างกันและการทดสอบหน่วย


ฉันเดาว่าคุณคงดัดแปลงมาจากหนังสือ "xUnit Test Patterns" ของเจอราร์ดเมสซารอส xunitpatterns.com
Spoike

ใช่คุณพูดถูก ฉันจะล้างสิ่งนั้นในโพสต์
Mendelt

คะแนนที่ยอดเยี่ยม การทดสอบหน่วยจะมีประโยชน์มาก แต่สิ่งสำคัญอย่างยิ่งที่จะต้องหลีกเลี่ยงการตกอยู่ในกับดักของการทดสอบหน่วยที่ซับซ้อนและพึ่งพาซึ่งกันและกันซึ่งจะสร้างภาษีมหาศาลสำหรับความพยายามใด ๆ ที่จะเปลี่ยนแปลงระบบ
ลิ่ม

9

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


9

คุณสมบัติบางประการของการทดสอบหน่วยที่ยอดเยี่ยม:

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

  • เมื่อคุณ refactor การทดสอบไม่ควรล้มเหลว

  • การทดสอบควรดำเนินไปอย่างรวดเร็วจนคุณไม่ลังเลที่จะเรียกใช้

  • การทดสอบทั้งหมดควรผ่านเสมอ ไม่มีผลลัพธ์ที่ไม่ได้กำหนด

  • การทดสอบหน่วยควรได้รับการพิจารณาอย่างดีเช่นเดียวกับรหัสการผลิตของคุณ

@Alotor: หากคุณแนะนำว่าไลบรารีควรมีการทดสอบหน่วยที่ API ภายนอกเท่านั้นฉันไม่เห็นด้วย ฉันต้องการทดสอบหน่วยสำหรับแต่ละชั้นเรียนรวมถึงชั้นเรียนที่ฉันไม่เปิดเผยต่อผู้โทรภายนอก (อย่างไรก็ตามหากฉันรู้สึกว่าจำเป็นต้องเขียนการทดสอบสำหรับวิธีการส่วนตัวฉันจำเป็นต้อง refactor ใหม่ )


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

ฉันไม่ใช้วิธีนั้น แต่ฉันใช้อุปกรณ์ทดสอบต่อสถานการณ์แทน นี่คือตัวอย่างคร่าวๆ:

[TestFixture]
public class StackTests
{
    [TestFixture]
    public class EmptyTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
        }

        [TestMethod]
        [ExpectedException (typeof(Exception))]
        public void PopFails()
        {
            _stack.Pop();
        }

        [TestMethod]
        public void IsEmpty()
        {
            Assert(_stack.IsEmpty());
        }
    }

    [TestFixture]
    public class PushedOneTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
            _stack.Push(7);
        }

        // Tests for one item on the stack...
    }
}

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

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

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

7

สิ่งที่คุณต้องการคือการวิเคราะห์พฤติกรรมของชั้นเรียนที่อยู่ระหว่างการทดสอบ

  1. การตรวจสอบพฤติกรรมที่คาดหวัง
  2. การตรวจสอบกรณีข้อผิดพลาด
  3. ความครอบคลุมของเส้นทางรหัสทั้งหมดภายในคลาส
  4. ใช้ฟังก์ชันสมาชิกทั้งหมดภายในชั้นเรียน

ความตั้งใจพื้นฐานคือเพิ่มความมั่นใจในพฤติกรรมของชั้นเรียน

สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อดูการปรับโครงสร้างโค้ดใหม่ Martin Fowler มีบทความที่น่าสนใจเกี่ยวกับการทดสอบที่เว็บไซต์ของเขา

HTH.

ไชโย

ปล้น


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

7

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


@Rismo ไม่ได้เป็นเอกสิทธิ์เฉพาะบุคคล ตามคำจำกัดความสิ่งที่ Quarrelsome เขียนไว้ที่นี่เป็นเอกสิทธิ์ของวิธีการ "Test First" ซึ่งเป็นส่วนหนึ่งของ TDD TDD ยังคำนึงถึงการปรับโครงสร้างใหม่ด้วย คำจำกัดความ "smarty pants" ที่สุดที่ฉันเคยอ่านคือ TDD = Test First + Refactor
Spoike

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

6

ฉันชอบตัวย่อ BICEP ที่ถูกต้องจากหนังสือPragmatic Unit Testingข้างต้น:

  • ขวา : ผลลัพธ์ถูกต้องหรือไม่?
  • B : ทั้งหมดคือเงื่อนไข oundary ถูกต้องหรือไม่
  • ฉัน : เราสามารถตรวจสอบฉัน nverse ความสัมพันธ์?
  • C : เราสามารถcผล Ross-การตรวจสอบโดยใช้วิธีการอื่น ๆ ?
  • E : เราสามารถบังคับให้อีเงื่อนไข rror ที่จะเกิดขึ้น?
  • P : ลักษณะความผิดปกติของpอยู่ในขอบเขตหรือไม่?

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


6

การทดสอบที่ดีจะต้องมีการบำรุงรักษา

ฉันยังไม่เข้าใจวิธีการทำเช่นนี้สำหรับสภาพแวดล้อมที่ซับซ้อน

หนังสือเรียนทั้งหมดเริ่มไม่ติดกาวเมื่อฐานรหัสของคุณเริ่มเข้าถึงรหัสหลายร้อย 1,000 หรือหลายล้านบรรทัด

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

สถาปัตยกรรมที่ดีสามารถควบคุมการระเบิดของปฏิสัมพันธ์ได้ แต่หลีกเลี่ยงไม่ได้เมื่อระบบมีความซับซ้อนมากขึ้นระบบทดสอบอัตโนมัติก็เติบโตไปพร้อมกับมัน

นี่คือจุดเริ่มต้นที่คุณต้องจัดการกับการแลกเปลี่ยน:

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

คุณต้องตัดสินใจ:

คุณเก็บกรณีทดสอบไว้ที่ไหนในฐานรหัสของคุณ

  • คุณจัดทำเอกสารกรณีทดสอบของคุณอย่างไร
  • สามารถใช้อุปกรณ์ทดสอบซ้ำเพื่อประหยัดการบำรุงรักษากรณีทดสอบได้หรือไม่
  • จะเกิดอะไรขึ้นเมื่อการดำเนินการกรณีทดสอบทุกคืนล้มเหลว ใครเป็นคนตรวจสอบ?
  • คุณดูแลรักษาวัตถุจำลองได้อย่างไร? หากคุณมีโมดูลทั้งหมด 20 โมดูลโดยใช้ API การบันทึกแบบจำลองการเปลี่ยน API จะกระเพื่อมอย่างรวดเร็ว กรณีทดสอบไม่เพียง แต่เปลี่ยน แต่วัตถุจำลอง 20 ชิ้นก็เปลี่ยนไป โมดูลทั้ง 20 โมดูลเหล่านี้ถูกเขียนขึ้นโดยทีมงานต่างๆมากมายเป็นเวลาหลายปี ปัญหาการใช้ซ้ำแบบคลาสสิก
  • แต่ละคนและทีมของพวกเขาเข้าใจถึงคุณค่าของการทดสอบอัตโนมัติพวกเขาไม่ชอบวิธีที่ทีมอื่นทำ :-)

ฉันสามารถไปได้ตลอดไป แต่ประเด็นของฉันคือ:

การทดสอบต้องสามารถบำรุงรักษาได้


5

ฉันได้กล่าวถึงหลักการเหล่านี้ในบทความ MSDN Magazineซึ่งฉันคิดว่าสำคัญสำหรับนักพัฒนาทุกคนที่จะอ่าน

วิธีที่ฉันกำหนดการทดสอบหน่วย "ดี" คือหากมีคุณสมบัติสามประการต่อไปนี้:

  • สามารถอ่านได้ (การตั้งชื่อการยืนยันตัวแปรความยาวความซับซ้อน .. )
  • พวกเขาสามารถบำรุงรักษาได้ (ไม่มีตรรกะไม่เกินที่ระบุอิงสถานะ refactored .. )
  • พวกเขาน่าเชื่อถือ (ทดสอบสิ่งที่ถูกต้องแยกไม่ใช่การทดสอบบูรณาการ .. )

รอยฉันเห็นด้วยสุดใจ สิ่งเหล่านี้มีความสำคัญมากกว่าความคุ้มครองกรณีขอบ
Matt Hinze

น่าไว้วางใจ - ยอดเยี่ยม!
ratkok

4
  • การทดสอบหน่วยเพียงแค่ทดสอบ API ภายนอกของหน่วยของคุณคุณไม่ควรทดสอบพฤติกรรมภายใน
  • การทดสอบ TestCase แต่ละครั้งควรทดสอบหนึ่งวิธี (และเพียงวิธีเดียว) ใน API นี้
    • ควรมีกรณีทดสอบเพิ่มเติมสำหรับกรณีความล้มเหลว
  • ทดสอบความครอบคลุมของการทดสอบของคุณ: เมื่อหน่วยทดสอบแล้วควรดำเนินการ 100% ของบรรทัดภายในหน่วยนี้

2

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

ขอแสดงความนับถือ


1

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


1

ฉันสองคำตอบ "A TRIP" ยกเว้นว่าการทดสอบควรพึ่งพากัน !!!

ทำไม?

DRY - อย่าทำซ้ำตัวเอง - ใช้กับการทดสอบเช่นกัน! การพึ่งพาการทดสอบสามารถช่วย 1) ประหยัดเวลาในการตั้งค่า 2) ประหยัดทรัพยากรการติดตั้งและ 3) ระบุความล้มเหลว แน่นอนว่ากรอบการทดสอบของคุณรองรับการอ้างอิงชั้นหนึ่งเท่านั้น ไม่อย่างนั้นฉันยอมรับว่าพวกเขาแย่

ติดตามhttp://www.iam.unibe.ch/~scg/Research/JExample/


ฉันเห็นด้วยกับคุณ TestNG เป็นอีกกรอบที่อนุญาตให้มีการอ้างอิงได้อย่างง่ายดาย
Davide

0

บ่อยครั้งการทดสอบหน่วยจะขึ้นอยู่กับวัตถุจำลองหรือข้อมูลจำลอง ฉันชอบเขียนแบบทดสอบหน่วยสามประเภท:

  • การทดสอบหน่วย "ชั่วคราว": พวกเขาสร้างวัตถุ / ข้อมูลจำลองของตนเองและทดสอบการทำงานของพวกเขา แต่ทำลายทุกอย่างและไม่เหลือร่องรอย (เหมือนไม่มีข้อมูลในฐานข้อมูลทดสอบ)
  • การทดสอบหน่วย "ต่อเนื่อง": พวกเขาทดสอบฟังก์ชันภายในโค้ดของคุณในการสร้างอ็อบเจกต์ / ข้อมูลที่ฟังก์ชันขั้นสูงกว่าจำเป็นต้องใช้ในภายหลังสำหรับการทดสอบหน่วยของตนเอง (หลีกเลี่ยงไม่ให้ฟังก์ชันขั้นสูงเหล่านั้นสร้างขึ้นใหม่ทุกครั้งที่มีชุดของวัตถุ / ข้อมูลจำลองของตนเอง)
  • การทดสอบหน่วย "ต่อเนื่อง": การทดสอบหน่วยโดยใช้วัตถุจำลอง / ข้อมูลที่มีอยู่แล้ว (เนื่องจากสร้างขึ้นในเซสชันการทดสอบหน่วยอื่น) โดยการทดสอบหน่วยต่อเนื่อง

ประเด็นคือหลีกเลี่ยงที่จะเล่นซ้ำทุกอย่างเพื่อให้สามารถทดสอบทุกฟังก์ชันได้

  • ฉันเรียกใช้ประเภทที่สามบ่อยมากเพราะมีวัตถุ / ข้อมูลจำลองทั้งหมดอยู่แล้ว
  • ฉันเรียกใช้ประเภทที่สองทุกครั้งที่โมเดลของฉันเปลี่ยนไป
  • ฉันเรียกใช้ตัวแรกเพื่อตรวจสอบฟังก์ชั่นพื้นฐานนาน ๆ ครั้งเพื่อตรวจสอบการถอยหลังพื้นฐาน

0

ลองนึกถึงการทดสอบ 2 ประเภทและปฏิบัติแตกต่างกัน - การทดสอบการทำงานและการทดสอบประสิทธิภาพ

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


แล้วการทดสอบหน่วยล่ะ?
Spoike

0

ฉันใช้หลักการตั้งชื่อการทดสอบที่สอดคล้องกันซึ่งอธิบายโดยมาตรฐานการตั้งชื่อการทดสอบหน่วยของ Roy Osheroveแต่ละวิธีในคลาสกรณีทดสอบที่กำหนดมีลักษณะการตั้งชื่อดังต่อไปนี้ MethodUnderTest_Scenario_ExpectedResult

    ส่วนชื่อการทดสอบแรกคือชื่อของวิธีการในระบบที่กำลังทดสอบ
    ต่อไปคือสถานการณ์เฉพาะที่กำลังทดสอบ
    สุดท้ายคือผลลัพธ์ของสถานการณ์นั้น

แต่ละส่วนใช้ Upper Camel Case และคั่นด้วยคะแนนต่ำกว่า

ฉันพบว่าสิ่งนี้มีประโยชน์เมื่อฉันเรียกใช้การทดสอบการทดสอบจะถูกจัดกลุ่มตามชื่อของวิธีการภายใต้การทดสอบ และมีการประชุมที่อนุญาตให้นักพัฒนาคนอื่น ๆ เข้าใจเจตนาในการทดสอบ

ฉันยังผนวกพารามิเตอร์เข้ากับชื่อวิธีการหากวิธีการภายใต้การทดสอบมีมากเกินไป

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