วิธีทดสอบหน่วยฟังก์ชันที่ปรับโครงสร้างรูปแบบกลยุทธ์ใหม่แล้วหรือไม่


10

ถ้าฉันมีฟังก์ชั่นในรหัสของฉันที่จะไป:

class Employee{

    public string calculateTax(string name, int salary)
    {
        switch (name)
        {
            case "Chris":
                doSomething($salary);
            case "David":
                doSomethingDifferent($salary);
            case "Scott":
               doOtherThing($salary);               
       }
}

โดยปกติฉันจะ refactor นี้เพื่อใช้ Ploymorphism โดยใช้คลาสโรงงานและรูปแบบกลยุทธ์:

public string calculateTax(string name)
{
    InameHandler nameHandler = NameHandlerFactory::getHandler(name);
    nameHandler->calculateTax($salary);
}

ตอนนี้ถ้าฉันใช้ TDD ฉันก็จะมีการทดสอบบางอย่างที่ทำงานกับต้นฉบับcalculateTax()ก่อนที่จะทำการรีแฟคเตอร์

อดีต:

calculateTax_givenChrisSalaryBelowThreshold_Expect111(){}    
calculateTax_givenChrisSalaryAboveThreshold_Expect111(){}

calculateTax_givenDavidSalaryBelowThreshold_Expect222(){}   
calculateTax_givenDavidSalaryAboveThreshold_Expect222(){} 

calculateTax_givenScottSalaryBelowThreshold_Expect333(){}
calculateTax_givenScottSalaryAboveThreshold_Expect333(){}

ฉันจะมีคลาส Factory NameHandlerFactoryและมีการใช้งานอย่างน้อย 3 InameHandlerครั้ง

ฉันจะทำการทดสอบซ้ำอีกครั้งได้อย่างไร ฉันควรลบการทดสอบหน่วยclaculateTax()จากEmployeeTestsและสร้างคลาสการทดสอบสำหรับการติดตั้งแต่ละครั้งInameHandlerหรือไม่

ฉันควรทดสอบคลาสของโรงงานด้วยหรือไม่

คำตอบ:


6

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

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

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

ปรับปรุง

อาจมีการทำซ้ำระหว่างการทดสอบของcalculateTax(เรียกว่าการทดสอบระดับสูง ) และการทดสอบสำหรับกลยุทธ์การคำนวณรายบุคคล ( การทดสอบระดับต่ำ ) - ขึ้นอยู่กับการใช้งานของคุณ

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

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

ฉันควรทดสอบคลาสของโรงงานด้วยหรือไม่

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


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

@Songo โปรดดูการปรับปรุงของฉัน
PéterTörök

5

ฉันจะเริ่มต้นด้วยการบอกว่าฉันไม่มีความเชี่ยวชาญในการทดสอบ TDD หรือหน่วย แต่นี่คือวิธีที่ฉันจะทดสอบนี้ (ฉันจะใช้รหัสเหมือนหลอก):

CalculateTaxDelegatesToNameHandler()
{
    INameHandlerFactory fakeNameHandlerFactory = Fake(INameHandlerFactory);
    INameHandler fakeNameHandler = Fake(INameHandler);

    A.Call.To(fakeNameHandlerFactory.getHandler("John")).Returns(fakeNameHandler);

    Employee employee = new Employee(fakeNameHandlerFactory);
    employee.CalculateTax("John");

    Assert.That.WasCalled(fakeNameHandler.calculateTax());
}

ดังนั้นผมจึงต้องการทดสอบว่าcalculateTax()วิธีการของการเรียนการทำงานของพนักงานได้อย่างถูกต้องขอให้มันNameHandlerFactoryหาNameHandlerแล้วเรียกว่าวิธีการของการกลับมาcalculateTax()NameHandler


hmmmm ดังนั้นคุณหมายถึงฉันควรทำการทดสอบการทดสอบพฤติกรรมแทน (การทดสอบที่เรียกว่าฟังก์ชั่นบางอย่าง) และทำการยืนยันค่าในชั้นเรียนที่ได้รับมอบหมาย?
Songo

ใช่นั่นคือสิ่งที่ฉันจะทำ ฉันจะเขียนการทดสอบแยกต่างหากสำหรับ NameHandlerFactory และ NameHandler เมื่อคุณมีสิ่งเหล่านี้ไม่มีเหตุผลที่จะทดสอบการทำงานของพวกเขาอีกครั้งในEmployee.calculateTax()วิธีการ ด้วยวิธีนี้คุณไม่จำเป็นต้องเพิ่มการทดสอบพนักงานพิเศษเมื่อคุณแนะนำ NameHandler ใหม่
Kristof Claes

3

คุณกำลังเรียนหนึ่งคลาส (พนักงานที่ทำทุกอย่าง) และสร้างคลาสได้ 3 กลุ่ม: โรงงานพนักงาน (ที่มีเพียงกลยุทธ์) และกลยุทธ์

ดังนั้นทำการทดสอบ 3 กลุ่ม:

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

แน่นอนว่าคุณสามารถทำการทดสอบอัตโนมัติสำหรับ shebang ทั้งหมด แต่ตอนนี้การทดสอบการรวมเป็นมากกว่าและควรได้รับการปฏิบัติเช่นนี้


2

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

กว่าที่ฉันจะใช้โรงงานและทำการทดสอบต่อไปสำหรับการติดตั้งแต่ละครั้ง

ในที่สุดฉันจะลบการทดสอบเก่าออก


2

ความคิดเห็นของฉันคือคุณไม่ควรทำอะไรเลยหมายความว่าคุณไม่ควรเพิ่มการทดสอบใหม่

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

ฉันเจอปัญหานี้หลายครั้งเมื่อใช้ TDD ฉันคิดว่าเหตุผลหลักคือวัตถุกลยุทธ์ไม่ใช่การพึ่งพาตามธรรมชาติแทนที่จะพูดว่าการพึ่งพาขอบเขตสถาปัตยกรรมเช่นทรัพยากรภายนอก (ไฟล์, DB, บริการระยะไกล ฯลฯ ) เนื่องจากไม่ใช่การพึ่งพาตามธรรมชาติฉันมักจะไม่ยึดพฤติกรรมของชั้นเรียนของฉันกับกลยุทธ์นี้ สัญชาตญาณของฉันคือฉันควรเปลี่ยนการทดสอบถ้าความคาดหวังจากชั้นเรียนของฉันเปลี่ยนไป

มีดีเป็นโพสต์จากลุงบ๊อบที่พูดถึงว่าเกี่ยวกับปัญหานี้เมื่อใช้ TDD

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

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