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


17

นี่คือกฎของ Robert C. Martin สำหรับ TDD :

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

เมื่อฉันเขียนการทดสอบที่ดูเหมือนว่าคุ้มค่า แต่ผ่านได้โดยไม่ต้องเปลี่ยนรหัสการผลิต:

  1. นั่นหมายความว่าฉันทำอะไรผิดหรือเปล่า?
  2. ฉันควรหลีกเลี่ยงการเขียนการทดสอบดังกล่าวในอนาคตหากสามารถช่วยได้หรือไม่?
  3. ฉันควรออกจากการทดสอบนั้นหรือลบออกหรือไม่

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


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

คำตอบ:


21

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

บางครั้งเราเขียนการทดสอบเพื่อพิสูจน์ทฤษฎี การทดสอบผ่านและที่พิสูจน์ทฤษฎีของเรา จากนั้นเราจะไม่ลบการทดสอบออก อย่างไรก็ตามเราอาจ (รู้ว่าเรามีการสำรองข้อมูลของการควบคุมแหล่งที่มา) ทำลายรหัสการผลิตเพื่อให้แน่ใจว่าเราเข้าใจว่าทำไมมันผ่านเมื่อเราไม่ได้คาดหวัง

หากปรากฏว่าเป็นการทดสอบที่ถูกต้องและไม่ถูกต้องและไม่ได้ทำการทดสอบซ้ำที่มีอยู่ให้ปล่อยไว้ที่นั่น


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

13

มันหมายความว่าอย่างใดอย่างหนึ่ง:

  1. คุณเขียนรหัสการผลิตที่ตอบสนองคุณสมบัติที่คุณต้องการโดยไม่ต้องเขียนการทดสอบก่อน (การละเมิด "TDD ทางศาสนา") หรือ
  2. คุณลักษณะที่คุณต้องการเกิดขึ้นเพื่อให้เป็นจริงตามรหัสการผลิตและคุณเพียงแค่เขียนการทดสอบหน่วยอื่นเพื่อครอบคลุมคุณลักษณะนั้น

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

public void TestAddMethod()
{
    Assert.IsTrue(Add(2,3) == 5);
}

เพราะสิ่งที่คุณต้องการจริงๆคือผลลัพธ์ของการรวม 2 และ 3 เข้าด้วยกัน

วิธีการใช้งานของคุณจะเป็น:

public int add(int x, int y)
{
    return x + y;
}

แต่สมมุติว่าตอนนี้ฉันต้องเพิ่ม 4 และ 6 เข้าด้วยกัน:

public void TestAddMethod2()
{
    Assert.IsTrue(Add(4,6) == 10);
}

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

ทีนี้สมมติว่าฉันพบว่าฟังก์ชั่นการเพิ่มของฉันต้องคืนค่าตัวเลขที่มีเพดานบ้างสมมุติว่า 100 ฉันสามารถเขียนวิธีการใหม่ที่ทดสอบสิ่งนี้:

public void TestAddMethod3()
{
    Assert.IsTrue(Add(100,100) == 100);
}

และการทดสอบนี้จะล้มเหลว ตอนนี้ฉันต้องเขียนฟังก์ชั่นใหม่

public int add(int x, int y)
{
    var a = x + y;
    return a > 100 ? 100 : a;
}

เพื่อให้มันผ่านไป

สามัญสำนึกสั่งการว่าถ้า

public void TestAddMethod2()
{
    Assert.IsTrue(Add(4,6) == 10);
}

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


5
ถ้าคุณทำตามตัวอย่างของมาร์ตินอย่างเต็มที่ (และเขาไม่จำเป็นต้องแนะนำให้คุณทำ) เพื่อที่จะadd(2,3)ผ่านคุณจะต้องส่งคืน 5. ฮาร์ดโค้ด จากนั้นคุณจะเขียนบททดสอบadd(4,6)ที่จะบังคับให้คุณเขียนรหัสการผลิตที่ทำให้มันผ่านไปโดยไม่ทำลายadd(2,3)ในเวลาเดียวกัน คุณจะจบลงด้วยreturn x + yแต่คุณจะไม่เริ่มต้นด้วย ในทางทฤษฎี โดยธรรมชาติแล้วมาร์ติน (หรืออาจเป็นคนอื่นฉันจำไม่ได้) ชอบที่จะให้ตัวอย่างเช่นเพื่อการศึกษา แต่ไม่ได้คาดหวังว่าคุณจะเขียนรหัสเล็ก ๆ น้อย ๆ เช่นนั้น
Anthony Pegram

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

2
@tietyT คุณจะต้องทำแบบทดสอบจนกว่าคุณจะทำ :)
Anthony Pegram

4
มีความเป็นไปได้ที่สามและมันขัดกับตัวอย่างของคุณ: คุณเขียนข้อสอบซ้ำ หากคุณติดตาม TDD "อย่างเคร่งครัด" การทดสอบใหม่ที่ผ่านจึงเป็นธงสีแดงเสมอ การติดตามDRYคุณไม่ควรเขียนการทดสอบสองรายการที่ทดสอบในสิ่งเดียวกัน
congusbongus

1
"ถ้าคุณทำตามตัวอย่างของมาร์ตินอย่างเต็มที่ (และเขาไม่จำเป็นต้องแนะนำให้คุณทำ) เพื่อเพิ่มการส่ง (2,3) คุณจะได้รับ 5 รหัสยาก" - นี่เป็นบิตของ TDD ที่เข้มงวดซึ่งมักจะมีกับฉันเสมอความคิดที่ว่าคุณเขียนโค้ดที่คุณรู้ว่ามันผิดในความคาดหวังของการทดสอบในอนาคตที่จะมาถึงและพิสูจน์มัน จะเกิดอะไรขึ้นถ้าการทดสอบในอนาคตนั้นไม่เคยเขียนด้วยเหตุผลบางอย่างและเพื่อนร่วมงานสันนิษฐานว่า "all-tests-green" หมายถึง "all-code-correct"?
Julia Hayward

2

ผ่านการทดสอบของคุณ แต่คุณไม่ผิด ฉันคิดว่ามันเกิดขึ้นเพราะรหัสการผลิตไม่ใช่ TDD ตั้งแต่ต้น

ลองสมมติว่า canonical (?) TDD ไม่มีรหัสการผลิต แต่มีบางกรณีทดสอบ (ซึ่งแน่นอนว่าล้มเหลวเสมอ) เราเพิ่มรหัสการผลิตที่จะผ่าน จากนั้นหยุดที่นี่เพื่อเพิ่มกรณีทดสอบที่ล้มเหลวมากขึ้น เพิ่มรหัสการผลิตที่จะผ่านอีกครั้ง

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

โดยส่วนตัวฉันไม่ชอบกฎเผด็จการและไร้มนุษยธรรมเช่นนั้น ((


2

จริงๆแล้วปัญหาเดียวกันก็เกิดขึ้นในโดโจเมื่อคืนนี้

ฉันทำวิจัยอย่างรวดเร็วเกี่ยวกับเรื่องนี้ นี่คือสิ่งที่ฉันมาด้วย:

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

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

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

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


0

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

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

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

โปรแกรมเมอร์คนแรกเขียน:

assert abs(-88888) == 88888
assert abs(-12345) == 12345
assert abs(-5000) == 5000
assert abs(-32) == 32
assert abs(46) == 46
assert abs(50) == 50
assert abs(5001) == 5001
assert abs(999999) == 999999
...

โปรแกรมเมอร์ที่สองเขียน:

assert abs(-1) == 1
assert abs(0) == 0
assert abs(1) == 1

การใช้งานโปรแกรมเมอร์ครั้งแรกอาจส่งผลให้:

def abs(n):
    if n < 0:
        return -n
    elif n > 0:
        return n

การใช้งานโปรแกรมเมอร์ที่สองอาจส่งผลให้:

def abs(n):
    if n < 0:
        return -n
    else:
        return n

การทดสอบทั้งหมดผ่านไป แต่โปรแกรมเมอร์คนแรกไม่เพียง แต่เขียนการทดสอบซ้ำซ้อนหลายครั้ง (โดยไม่จำเป็นต้องทำให้วงจรการพัฒนาช้าลง) โดยไม่จำเป็น แต่ยังไม่สามารถทดสอบกรณีขอบเขต ( abs(0))

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


โปรแกรมเมอร์คนที่สองก็ไม่สนใจการทดสอบเช่นกันเพราะเพื่อนร่วมงานของเขาได้นิยามใหม่abs(n) = n*nและผ่านไปแล้ว
Eiko

@Eiko คุณพูดถูก การเขียนแบบทดสอบน้อยเกินไปอาจทำให้คุณรู้สึกแย่ abs(-2)โปรแกรมเมอร์ที่สองคือตระหนี่มากเกินไปโดยไม่ได้อยู่ที่การทดสอบอย่างน้อย การกลั่นกรองเป็นสิ่งสำคัญเช่นเดียวกับทุกสิ่ง
คิด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.