Làm cách nào tôi có thể thiết kế các trường hợp thử nghiệm để bao gồm mã dựa trên các sự kiện ngẫu nhiên?


15

Ví dụ: nếu mã tạo một int ngẫu nhiên từ 0-10 và lấy một nhánh khác nhau trên mỗi kết quả, làm thế nào người ta có thể thiết kế một bộ kiểm tra để đảm bảo phạm vi tuyên bố 100% trong mã đó?

Trong Java, mã có thể là một cái gì đó như:

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

Câu trả lời:


22

Mở rộng câu trả lời của David, người mà tôi hoàn toàn đồng ý với việc bạn nên tạo một trình bao bọc cho Ngẫu nhiên. Tôi đã viết khá nhiều câu trả lời tương tự về nó trước đó trong một câu hỏi tương tự vì vậy đây là "phiên bản ghi chú của Cliff".

Điều bạn nên làm là trước tiên tạo trình bao bọc dưới dạng giao diện (hoặc lớp trừu tượng):

public interface IRandomWrapper {
    int getInt();
}

Và lớp cụ thể cho điều này sẽ trông như thế này:

public RandomWrapper implements IRandomWrapper {

    private Random random;

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

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

}

Nói rằng lớp học của bạn là như sau:

class MyClass {

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

}

Để sử dụng IRandomWrapper một cách chính xác, bạn cần sửa đổi lớp của mình để lấy nó làm thành viên (thông qua hàm tạo hoặc trình cài đặt):

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
        }
    }

}

Bây giờ bạn có thể kiểm tra hành vi của lớp mình với trình bao bọc, bằng cách chế nhạo trình bao bọc. Bạn có thể làm điều này với một khung mô phỏng, nhưng điều này cũng dễ thực hiện:

public class MockedRandomWrapper implements IRandomWrapper {

   private int theInt;    

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

   public int getInt() { 
       return theInt;
   }

}

Vì lớp học của bạn mong đợi một cái gì đó trông giống như một IRandomWrapper bây giờ, bạn có thể sử dụng cái bị chế giễu để ép buộc hành vi trong bài kiểm tra của bạn. Dưới đây là một số ví dụ về các bài kiểm tra 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
}

Hi vọng điêu nay co ich.


3
Hoàn toàn đồng ý với điều này. Bạn kiểm tra một sự kiện ngẫu nhiên bằng cách loại bỏ tính chất ngẫu nhiên của sự kiện. Lý thuyết tương tự có thể được sử dụng cho dấu thời gian
Richard

3
Lưu ý: tehcnique này, về việc đưa cho một đối tượng đối tượng khác mà nó cần, thay vì để anh ta kích thích chúng, được gọi là Dependency Injection
Clement Herreman

23

Bạn có thể (nên) bọc mã tạo ngẫu nhiên trong một lớp hoặc phương thức và sau đó mô phỏng / ghi đè mã đó trong các thử nghiệm để đặt giá trị bạn muốn, để các thử nghiệm của bạn có thể dự đoán được.


5

Bạn đã có một phạm vi được chỉ định (0-10) và độ chi tiết được chỉ định (toàn bộ số). Vì vậy, khi kiểm tra, bạn không kiểm tra với các số ngẫu nhiên. Bạn kiểm tra trong một vòng lặp lần lượt đánh vào từng trường hợp. Tôi khuyên bạn nên chuyển số ngẫu nhiên vào một hàm phụ có chứa câu lệnh tình huống, cho phép bạn chỉ kiểm tra hàm phụ.


tốt hơn nhiều (vì đơn giản hơn) so với những gì tôi đề xuất, ước gì tôi có thể chuyển upvote của mình :)
David

Thật ra bạn nên làm cả hai. Kiểm tra bằng RandomObject giả để kiểm tra từng nhánh riêng lẻ kiểm tra lặp lại với RandomObject thực. Cái trước là một bài kiểm tra đơn vị, cái sau giống như một bài kiểm tra integrationt.
sleske

3

Bạn có thể sử dụng thư viện PowerMock để giả định lớp Random và khai thác phương thức nextInt () của nó để trả về giá trị mong đợi. Không cần thay đổi mã gốc nếu bạn không muốn.

Tôi đang sử dụng PowerMockito và vừa thử nghiệm một phương pháp tương tự như của bạn. Đối với mã mà bạn đã đăng bài kiểm tra JUnit sẽ trông giống như thế này:

@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
     }

    (...)

Bạn cũng có thể ngắt cuộc gọi nextInt (int) để nhận bất kỳ tham số nào, trong trường hợp bạn muốn thêm nhiều trường hợp khác tại tổng đài của mình:

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

Khá, phải không? :)


2

Sử dụng QuickCheck ! Tôi mới bắt đầu chơi với điều này gần đây và nó thật tuyệt vời. Giống như hầu hết các ý tưởng hay, nó xuất phát từ Haskell, nhưng ý tưởng cơ bản là thay vì đưa ra các trường hợp kiểm tra đóng hộp thử nghiệm của bạn, bạn hãy để trình tạo số ngẫu nhiên xây dựng chúng cho bạn. Bằng cách đó, thay vì 4 - 6 trường hợp mà bạn có thể nghĩ ra trong xUnit, bạn có thể cho máy tính thử hàng trăm hoặc hàng nghìn đầu vào và xem những trường hợp nào không tuân thủ các quy tắc bạn đã đặt.

Ngoài ra QuickCheck sẽ khi nó tìm thấy một trường hợp thất bại hãy cố gắng đơn giản hóa nó để nó có thể tìm thấy trường hợp đơn giản nhất có thể không thành công. (Và tất nhiên khi bạn tìm thấy một trường hợp thất bại, bạn cũng có thể xây dựng nó thành một bài kiểm tra xUnit)

Dường như có ít nhất hai phiên bản cho Java để phần đó không phải là vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.