ฉันจะออกแบบเคสทดสอบเพื่อให้ครอบคลุมโค้ดตามเหตุการณ์สุ่มได้อย่างไร


15

ตัวอย่างเช่นหากรหัสสร้าง int แบบสุ่มจาก 0-10 และรับสาขาที่แตกต่างกันในแต่ละผลลัพธ์หนึ่งจะออกแบบชุดทดสอบเพื่อรับประกันความครอบคลุมคำสั่ง 100% ในรหัสดังกล่าวได้อย่างไร

ใน Java โค้ดอาจมีลักษณะดังนี้:

int i = new Random().nextInt(10);
switch(i)
{
    //11 case statements
}

คำตอบ:


22

ขยายคำตอบของเดวิดซึ่งฉันเห็นด้วยอย่างยิ่งกับการที่คุณควรสร้างเสื้อคลุมสำหรับการสุ่ม ฉันเขียนคำตอบเดียวกันนี้ก่อนหน้านี้ในคำถามที่คล้ายกันดังนั้นนี่คือ "Cliff's notes version" ของมัน

สิ่งที่คุณควรทำคือการสร้าง wrapper ก่อนเป็นส่วนต่อประสาน (หรือคลาสนามธรรม):

public interface IRandomWrapper {
    int getInt();
}

และคลาสคอนกรีตสำหรับสิ่งนี้จะมีลักษณะเช่นนี้:

public RandomWrapper implements IRandomWrapper {

    private Random random;

    public RandomWrapper() {
        random = new Random();
    }

    public int getInt() {
        return random.nextInt(10);
    }

}

สมมติว่าคลาสของคุณดังต่อไปนี้:

class MyClass {

    public void doSomething() {
        int i=new Random().nextInt(10)
        switch(i)
        {
            //11 case statements
        }
    }

}

ในการใช้ IRandomWrapper อย่างถูกต้องคุณต้องแก้ไขคลาสของคุณเพื่อรับเป็นสมาชิก (ผ่าน Constructor หรือ Setter):

public class MyClass {

    private IRandomWrapper random = new RandomWrapper(); // default implementation

    public setRandomWrapper(IRandomWrapper random) {
        this.random = random;
    }

    public void doSomething() {
        int i = random.getInt();
        switch(i)
        {
            //11 case statements
        }
    }

}

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

public class MockedRandomWrapper implements IRandomWrapper {

   private int theInt;    

   public MockedRandomWrapper(int theInt) {
       this.theInt = theInt;
   }

   public int getInt() { 
       return theInt;
   }

}

เนื่องจากชั้นเรียนของคุณคาดหวังบางสิ่งที่ดูเหมือนว่าIRandomWrapperตอนนี้คุณสามารถใช้สิ่งที่เย้ยหยันเพื่อบังคับพฤติกรรมในการทดสอบของคุณ นี่คือตัวอย่างของการทดสอบ JUnit:

@Test
public void testFirstSwitchStatement() {
    MyClass mc = new MyClass();
    IRandomWrapper random = new MockedRandomWrapper(0);
    mc.setRandomWrapper(random);

    mc.doSomething();

    // verify the behaviour for when random spits out zero
}

@Test
public void testFirstSwitchStatement() {
    MyClass mc = new MyClass();
    IRandomWrapper random = new MockedRandomWrapper(1);
    mc.setRandomWrapper(random);

    mc.doSomething();

    // verify the behaviour for when random spits out one
}

หวังว่านี่จะช่วยได้


3
เห็นด้วยกับสิ่งนี้โดยสิ้นเชิง คุณทดสอบเหตุการณ์แบบสุ่มโดยการเอาลักษณะแบบสุ่มของเหตุการณ์ ทฤษฎีเดียวกันนี้สามารถใช้กับการประทับเวลาได้
Richard

3
หมายเหตุ: tehcnique นี้ของการให้วัตถุวัตถุอื่น ๆ ที่มันต้องการแทนที่จะปล่อยให้เขา instanciate พวกเขาเรียกว่าการพึ่งพาการฉีด
Clement Herreman

23

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


5

คุณมีช่วงที่ระบุ (0-10) และรายละเอียดที่ระบุ (จำนวนเต็ม) ดังนั้นเมื่อทำการทดสอบคุณจะไม่ทดสอบกับตัวเลขสุ่ม คุณทดสอบภายในการวนซ้ำที่กระทบแต่ละกรณี ฉันแนะนำให้ส่งตัวเลขสุ่มไปยังฟังก์ชั่นย่อยที่มีคำสั่ง case ซึ่งช่วยให้คุณทดสอบฟังก์ชันย่อยได้


ดีมาก (เพราะง่ายขึ้น) กว่าสิ่งที่ฉันแนะนำหวังว่าฉันจะโอน upvotes ของฉัน :)
เดวิด

ที่จริงคุณควรทำทั้งสองอย่าง ทดสอบกับ RandomObject จำลองเพื่อทดสอบแต่ละสาขาและทดสอบซ้ำ ๆ ด้วย RandomObject จริง อดีตคือการทดสอบหน่วยหลังเป็นเหมือนการทดสอบการรวมกลุ่ม
sleske

3

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

ฉันใช้ PowerMockito และทดสอบวิธีที่คล้ายกับของคุณ สำหรับรหัสที่คุณโพสต์การทดสอบ JUnit ควรมีลักษณะดังนี้:

@RunWith(PowerMockRunner.class)
@PrepareForTest( { Random.class, ClassUsingRandom.class } ) // Don't forget to prepare the Random class! :)

public void ClassUsingRandomTest() {

    ClassUsingRandom cur;
    Random mockedRandom;

    @Before
    public void setUp() throws Exception {

        mockedRandom = PowerMockito.mock(Random.class);

        // Replaces the construction of the Random instance in your code with the mock.
        PowerMockito.whenNew(Random.class).withNoArguments().thenReturn(mockedRandom);

        cur = new ClassUsingRandom();
    }

    @Test
    public void testSwitchAtZero() {

        PowerMockito.doReturn(0).when(mockedRandom).nextInt(10);

        cur.doSomething();

        // Verify behaviour at case 0
     }

    @Test
    public void testSwitchAtOne() {

        PowerMockito.doReturn(1).when(mockedRandom).nextInt(10);

        cur.doSomething();

        // Verify behaviour at case 1
     }

    (...)

นอกจากนี้คุณยังสามารถ stub การเรียก nextInt (int) เพื่อรับพารามิเตอร์ใด ๆ ในกรณีที่คุณต้องการเพิ่มเคสเพิ่มเติมที่สวิตช์ของคุณ:

PowerMockito.doReturn(0).when(mockedRandom).nextInt(Mockito.anyInt());

น่ารักใช่มั้ย :)


2

ใช้QuickCheck ! ฉันเพิ่งเริ่มเล่นกับเรื่องนี้เมื่อเร็ว ๆ นี้และมันก็น่าอัศจรรย์ เช่นเดียวกับแนวคิดที่เจ๋ง ๆ ส่วนใหญ่มาจาก Haskell แต่แนวคิดพื้นฐานก็คือแทนที่จะให้เคสทดสอบแบบทดสอบกระป๋องของคุณคุณปล่อยให้เครื่องสร้างตัวเลขสุ่มของคุณสร้างให้คุณ ด้วยวิธีนี้แทนที่จะเป็น 4-6 รายที่คุณอาจคิดใน xUnit คุณสามารถให้คอมพิวเตอร์ลองใช้อินพุตเป็นร้อยหรือพันและดูว่าอันไหนไม่สอดคล้องกับกฎที่คุณตั้งไว้

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

ดูเหมือนว่าจะมีอย่างน้อยสองรุ่นสำหรับ Java เพื่อให้ส่วนนั้นไม่ควรมีปัญหา

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