การทดสอบ vs อย่าทำซ้ำตัวเอง (DRY)


11

ทำไมการทำซ้ำตัวเองด้วยการเขียนข้อสอบจึงได้รับการสนับสนุนอย่างมาก?

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

คำตอบ:


25

ฉันเชื่อว่านี่เป็นความเข้าใจผิดที่ฉันสามารถคิดได้

รหัสทดสอบที่ทดสอบรหัสการผลิตนั้นไม่เหมือนกันทั้งหมด ฉันจะสาธิตด้วยไพ ธ อน:

def multiply(a, b):
    """Multiply ``a`` by ``b``"""
    return a*b

จากนั้นการทดสอบอย่างง่ายจะเป็น:

def test_multiply():
    assert multiply(4, 5) == 20

ฟังก์ชั่นทั้งสองมีคำจำกัดความที่คล้ายกัน แต่ทั้งสองทำสิ่งที่แตกต่างกันมาก ไม่มีรหัสซ้ำกันที่นี่ ;-)

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

def test_multiply_1_and_3():
    """Assert that a multiplication of 1 and 3 is 3."""
    assert multiply(1, 3) == 3

def test_multiply_1_and_7():
    """Assert that a multiplication of 1 and 7 is 7."""
    assert multiply(1, 7) == 7

def test_multiply_3_and_4():
    """Assert that a multiplication of 3 and 4 is 12."""
    assert multiply(3, 4) == 12

ลองนึกภาพการทำเช่นนี้กับโค้ดที่มีประสิทธิภาพกว่า 1,000 บรรทัด แต่คุณทดสอบบนพื้นฐานของ 'คุณสมบัติ':

def test_multiply_positive():
    """Assert that positive numbers can be multiplied."""
    assert multiply(1, 3) == 3
    assert multiply(1, 7) == 7
    assert multiply(3, 4) == 12

def test_multiply_negative():
    """Assert that negative numbers can be multiplied."""
    assert multiply(1, -3) == -3
    assert multiply(-1, -7) == 7
    assert multiply(-3, 4) == -12

ตอนนี้เมื่อคุณสมบัติเพิ่ม / ลบฉันต้องพิจารณาเพิ่ม / ลบฟังก์ชั่นการทดสอบหนึ่ง

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


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

@ pink-diamond-square ฉันเห็นว่า NUnit ไม่หยุดการทดสอบหลังจากการยืนยันล้มเหลว (ซึ่งฉันคิดว่าแปลก) ในกรณีเฉพาะนั้นดีกว่าที่จะมีการยืนยันหนึ่งครั้งต่อการทดสอบ หากกรอบการทดสอบหน่วยจะหยุดการทดสอบหลังจากการยืนยันที่ล้มเหลวหลายการยืนยันที่ดีกว่า
siebz0r

3
NUnit ไม่ได้หยุดชุดทดสอบทั้งหมด แต่การทดสอบหนึ่งจะหยุดจนกว่าคุณจะทำตามขั้นตอนเพื่อป้องกัน (คุณสามารถตรวจจับข้อยกเว้นได้ซึ่งเป็นประโยชน์ในบางครั้ง) ประเด็นที่ฉันคิดว่าพวกเขากำลังทำคือถ้าคุณเขียนการทดสอบที่มีมากกว่าหนึ่งยืนยันคุณจะไม่ได้รับข้อมูลทั้งหมดที่คุณต้องการในการแก้ไขปัญหา ในการทำตามตัวอย่างลองจินตนาการว่าฟังก์ชั่นการคูณนี้ไม่ชอบหมายเลข 3 ในกรณีนี้assert multiply(1,3)จะล้มเหลว แต่คุณจะไม่ได้รับรายงานการทดสอบที่ล้มเหลวassert multiply(3,4)ด้วย
Rob Church

ฉันแค่คิดว่าฉันจะยกมันเพราะการยืนยันต่อการทดสอบเดียวคือจากสิ่งที่ฉันได้อ่านใน. net โลก "การปฏิบัติที่ดี" และการยืนยันที่หลากหลายคือ "การใช้ในทางปฏิบัติ" ดูเหมือนว่าจะแตกต่างกันเล็กน้อยในเอกสาร Pythonที่ตัวอย่างdef test_shuffleดำเนินการสองชุด
โบสถ์ Rob

ฉันเห็นด้วยและไม่เห็นด้วย: D มีการทำซ้ำอย่างชัดเจนที่นี่: assert multiply(*, *) == *ดังนั้นคุณสามารถกำหนดassert_multiplyฟังก์ชั่น ในสถานการณ์ปัจจุบันมันไม่สำคัญโดยการนับแถวและการอ่าน แต่การทดสอบที่ยาวนานคุณสามารถใช้การยืนยันที่ซับซ้อนการติดตั้งการสร้างรหัสการแข่งขัน ฯลฯ ... ฉันไม่รู้ว่านี่เป็นวิธีปฏิบัติที่ดีที่สุด แต่ฉันมักจะทำ นี้.
inf3rno

10

ดูเหมือนว่าการทดสอบโดยทั่วไปแสดงสิ่งเดียวกับรหัสและดังนั้นจึงซ้ำกัน

ไม่นี่ไม่เป็นความจริง

การทดสอบมีวัตถุประสงค์ที่แตกต่างจากการใช้งานของคุณ:

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

4

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


2

เป้าหมายสูงสุดของ DRY จะไม่รวมการกำจัดรหัสการทดสอบทั้งหมดหรือไม่

ไม่มีเป้าหมายสูงสุดของแห้งจะกำจัดจริงเฉลี่ยของทุกรหัสการผลิต

หากการทดสอบของเราเป็นข้อมูลจำเพาะที่สมบูรณ์แบบในสิ่งที่เราต้องการให้ระบบทำเราจะต้องสร้างรหัสการผลิตที่สอดคล้องกัน (หรือไบนารี) โดยอัตโนมัติโดยลบฐานการผลิตตามรหัสอย่างมีประสิทธิภาพ

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

ฉันไม่คิดว่าสิ่งที่ตรงกันข้าม (กำจัดการทดสอบทั้งหมด) เป็นที่ต้องการเพราะ:

  • คุณต้องแก้ไขความต้านทานที่ไม่ตรงกันระหว่างการนำไปใช้และข้อกำหนด รหัสการผลิตสามารถสื่อถึงความตั้งใจได้ในระดับหนึ่ง แต่มันจะไม่ง่ายอย่างที่จะให้เหตุผลเกี่ยวกับการทดสอบที่แสดงออกมาอย่างดี เรามนุษย์ต้องการมุมมองที่สูงขึ้นว่าทำไมเราจึงสร้างสิ่งต่าง ๆ แม้ว่าคุณจะไม่ทำการทดสอบเนื่องจาก DRY รายละเอียดอาจจะต้องเขียนลงในเอกสารอยู่ดีซึ่งเป็นสัตว์ร้ายที่อันตรายกว่าในแง่ของการจับคู่อิมพิแดนซ์และการถอดรหัสรหัสหากคุณถามฉัน
  • ในขณะที่รหัสการผลิตสามารถสืบหาได้ง่ายจากข้อกำหนดคุณสมบัติที่สามารถปฏิบัติการได้ที่ถูกต้อง (สมมติว่ามีเวลาเพียงพอ) ชุดทดสอบนั้นยากที่จะสร้างขึ้นใหม่จากรหัสสุดท้ายของโปรแกรม ข้อมูลจำเพาะไม่ปรากฏอย่างชัดเจนเพียงแค่ดูรหัสเพราะการโต้ตอบระหว่างหน่วยโค้ดที่รันไทม์นั้นยากที่จะทำออกมาได้ นี่คือเหตุผลที่เรามีช่วงเวลาที่ยากลำบากในการจัดการกับแอปพลิเคชันแบบดั้งเดิมที่ไม่มีการทดสอบ กล่าวอีกนัยหนึ่ง: หากคุณต้องการให้แอปพลิเคชันของคุณอยู่รอดได้นานกว่าสองสามเดือนคุณอาจเสียการใช้ฮาร์ดไดรฟ์ที่โฮสต์ฐานข้อมูลการผลิตของคุณได้ดีกว่าชุดทดสอบที่คุณใช้
  • การแนะนำข้อผิดพลาดในรหัสการผลิตง่ายกว่าในรหัสทดสอบ และเนื่องจากรหัสการผลิตไม่ได้ตรวจสอบตัวเอง (แม้ว่าจะสามารถติดต่อกับ Design by Contract หรือระบบประเภทที่สมบูรณ์ยิ่งขึ้น) เรายังคงต้องใช้โปรแกรมภายนอกเพื่อทดสอบและเตือนเราหากมีการถดถอยเกิดขึ้น

1

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


1

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

ตัวอย่างเช่นหากคุณมีฟังก์ชั่นMyFunctionที่เรียกว่าในรหัสการผลิตในที่เดียวและคุณเขียนการทดสอบ 20 หน่วยสำหรับมันคุณสามารถจบลงด้วยการมี 21 สถานที่ในรหัสของคุณที่เรียกว่าฟังก์ชั่น ตอนนี้เมื่อคุณต้องเปลี่ยนลายเซ็นของMyFunctionหรือความหมายหรือทั้งสองอย่าง (เนื่องจากข้อกำหนดบางประการมีการเปลี่ยนแปลง) คุณมี 21 ที่ที่จะเปลี่ยนแทนที่จะเป็นที่เดียว และเหตุผลก็คือการละเมิดหลักการ DRY: คุณเรียกใช้ฟังก์ชั่นเดิมซ้ำ ๆ (อย่างน้อย) ถึงMyFunction21 ครั้ง

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

"สถานที่ไม่กี่แห่ง" ยังคงเป็นมากกว่า "สถานที่แห่งเดียว" (สิ่งที่คุณได้รับโดยไม่มีการทดสอบหน่วยเลย) แต่ข้อดีของการมีการทดสอบหน่วยควรหนักกว่าข้อดีของการมีรหัสน้อยกว่าในการเปลี่ยนแปลง ไม่ถูกต้อง).


0

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

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

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

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

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


0

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


0

การทดสอบหน่วยไม่ควรรวมถึงการทำซ้ำรหัสภายใต้การทดสอบตามที่ได้รับการบันทึกแล้ว

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

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

ผู้สอนศาสนาท้องถิ่น TDD / BDD กล่าวแบบนี้:
"รหัสการผลิตของคุณควรเป็น DRY แต่มันก็โอเคสำหรับการทดสอบของคุณที่จะ 'ชื้น'


0

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

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

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

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