ตกลงเพื่อทำซ้ำรหัสสำหรับการทดสอบหน่วยหรือไม่


11

ฉันเขียนอัลกอริธึมการเรียงลำดับสำหรับการกำหนดชั้นเรียนและฉันยังได้เขียนการทดสอบบางอย่างเพื่อให้แน่ใจว่าอัลกอริทึมนั้นได้ดำเนินการอย่างถูกต้อง การทดสอบของฉันมีความยาวเพียง 10 บรรทัดและมี 3 ในนั้น แต่เพียง 1 บรรทัดที่เปลี่ยนแปลงระหว่าง 3 ดังนั้นจึงมีรหัสซ้ำจำนวนมาก มันจะดีกว่าที่จะ refactor รหัสนี้เป็นวิธีอื่นที่เรียกว่าจากการทดสอบแต่ละครั้งหรือไม่ ฉันจะไม่ต้องเขียนแบบทดสอบอื่นเพื่อทดสอบการเปลี่ยนโครงสร้างหรือไม่? ตัวแปรบางตัวสามารถเลื่อนขึ้นไปถึงระดับคลาสได้ การทดสอบคลาสและวิธีการควรเป็นไปตามกฎเดียวกันกับคลาส / วิธีการปกติหรือไม่

นี่คือตัวอย่าง:

    [TestMethod]
    public void MergeSortAssertArrayIsSorted()
    {
        int[] a = new int[1000];
        Random rand = new Random(DateTime.Now.Millisecond);
        for(int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
        int[] b = new int[1000];
        a.CopyTo(b, 0);
        List<int> temp = b.ToList();
        temp.Sort();
        b = temp.ToArray();

        MergeSort merge = new MergeSort();
        merge.mergeSort(a, 0, a.Length - 1);
        CollectionAssert.AreEqual(a, b);
    }
    [TestMethod]
    public void InsertionSortAssertArrayIsSorted()
    {
        int[] a = new int[1000];
        Random rand = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
        int[] b = new int[1000];
        a.CopyTo(b, 0);
        List<int> temp = b.ToList();
        temp.Sort();
        b = temp.ToArray();

        InsertionSort merge = new InsertionSort();
        merge.insertionSort(a);
        CollectionAssert.AreEqual(a, b); 
    }

คำตอบ:


21

รหัสการทดสอบยังคงเป็นรหัสและต้องได้รับการบำรุงรักษา

หากคุณต้องการเปลี่ยนลอจิกที่คัดลอกคุณจะต้องทำเช่นนั้นในทุกที่ที่คุณคัดลอกไปเป็นปกติ

ยังคงใช้แห้ง

ฉันจะไม่ต้องเขียนแบบทดสอบอื่นเพื่อทดสอบการเปลี่ยนโครงสร้างหรือไม่?

คุณจะ? และคุณจะรู้ได้อย่างไรว่าการทดสอบที่คุณมีอยู่นั้นถูกต้อง?

คุณทดสอบ refactoring โดยเรียกใช้การทดสอบ พวกเขาทุกคนควรมีผลลัพธ์ที่เหมือนกัน


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

6
ฉันไม่เห็นด้วย. การทดสอบไม่จำเป็นต้องเป็นแบบแห้งมันสำคัญกว่าสำหรับพวกเขาที่จะเป็นแบบ DAMP (วลีที่สื่อความหมายและมีความหมาย) มากกว่า DRY (โดยทั่วไปแล้วอย่างน้อยในกรณีเฉพาะนี้การดึงการกำหนดค่าเริ่มต้นซ้ำ ๆ ให้เป็นผู้ช่วยทำให้เข้าใจได้อย่างแน่นอน)
Jörg W Mittag

2
ฉันไม่เคยได้ยิน DAMP มาก่อน แต่ฉันชอบคำอธิบายนั้น
โจอาคิมซาวเออร์

@ Jörg W Mittag: คุณยังสามารถเป็น DRY และ DAMP ได้ด้วยการทดสอบ ฉันมักจะปรับโครงสร้างชิ้นส่วน ARRANGE-ACT-ASSERT (หรือ GIVEN-WHEN-THEN) ที่แตกต่างกันของการทดสอบเป็นวิธีการช่วยเหลือในการติดตั้งการทดสอบถ้าฉันรู้ว่าส่วนหนึ่งของการทดสอบซ้ำ พวกเขามักจะมีชื่อที่ชื้นเช่นและแม้กระทั่งเป็นง่ายๆเป็นgivenThereAreProductsSet(amount) actWith(param)ฉันจัดการกับ api ที่ชาญฉลาดได้อย่างคล่องแคล่ว (เช่นgivenThereAre(2).products()) หนึ่งครั้ง แต่ฉันหยุดอย่างรวดเร็วเพราะรู้สึกเหมือน overkill
Spoike

11

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

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

        int[] a = new int[1000];
        Random rand = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
        int[] b = new int[1000];
        a.CopyTo(b, 0);
        List<int> temp = b.ToList();
        temp.Sort();
        b = temp.ToArray();

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


4

ไม่มันไม่เป็นไร คุณควรใช้TestDataBuilderแทน คุณควรระมัดระวังในการอ่านของการทดสอบของคุณ:? 1,000 ข ถ้าพรุ่งนี้ต้องทำงานกับการนำไปใช้งานคุณกำลังทำการทดสอบการทดสอบเป็นวิธีที่ยอดเยี่ยมในการเข้าสู่ตรรกะ: เขียนการทดสอบของคุณให้กับเพื่อนโปรแกรมเมอร์ซึ่งไม่ใช่สำหรับคอมไพเลอร์ :)

นี่คือการดำเนินการทดสอบของคุณ "ปรับปรุงใหม่":

/**
* Data your tests will exercice on
*/
public class MyTestData(){
    final int [] values;
    public MyTestData(int sampleSize){
        values = new int[sampleSize];
        //Out of scope of your question : Random IS a depencency you should manage
        Random rand = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
    }
    public int [] values();
        return values;
    }

}

/**
* Data builder, with default value. 
*/
public class MyTestDataBuilder {
    //1000 is actually your sample size : emphasis on the variable name
    private int sampleSize = 1000; //default value of the sample zie
    public MyTestDataBuilder(){
        //nope
    }
    //this is method if you need to test with another sample size
    public MyTestDataBuilder withSampleSizeOf(int size){
        sampleSize=size;
    }

    //call to get an actual MyTestData instance
    public MyTestData build(){
        return new MyTestData(sampleSize);
    }
}

public class MergeSortTest { 

    /**
    * Helper method build your expected data
    */
    private int [] getExpectedData(int [] source){
        int[] expectedData =  Arrays.copyOf(source,source.length);
        Arrays.sort(expectedData);
        return expectedData;
    }
}

//revamped tests method Merge
    public void MergeSortAssertArrayIsSorted(){
        int [] actualData = new MyTestDataBuilder().build();
        int [] expected = getExpectedData(actualData);
        //Don't know what 0 is for. An option, that should have a explicit name for sure :)
        MergeSort merge = new MergeSort();
        merge.mergeSort(actualData,0,actualData.length-1); 
        CollectionAssert.AreEqual(actualData, expected);
    }

 //revamped tests method Insertion
 public void InsertionSortAssertArrayIsSorted()
    {
        int [] actualData = new MyTestDataBuilder().build();
        int [] expected = getExpectedData(actualData);
        InsertionSort merge = new InsertionSort();
        merge.insertionSort(actualData);
        CollectionAssert.AreEqual(actualData, expectedData); 
    }
//another Test, for which very small sample size matter
public void doNotCrashesWithEmptyArray()
    {
        int [] actualData = new MyTestDataBuilder().withSampleSizeOf(0).build();
        int [] expected = getExpectedData(actualData);
        //continue ...
    }
}

2

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


2

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

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

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

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