ランダムイベントに基づいてコードをカバーするテストケースを設計するにはどうすればよいですか?


15

たとえば、コードが0〜10のランダムなintを生成し、結果ごとに異なる分岐を取る場合、そのようなコードで100%のステートメントカバレッジを保証するテストスイートをどのように設計できますか。

Javaでは、コードは次のようになります。

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

回答:


22

ランダムのラッパーを作成する必要があることに完全に同意するDavidの答えを拡張します。ような質問で以前にそれについてほとんど同じ答えを書いたので、ここにそれの「クリフのノートバージョン」があります。

あなたがすべきことは、最初にラッパーをインターフェース(または抽象クラス)として作成することです:

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を正しく使用するには、クラスを変更して(コンストラクターまたはセッターを介して)メンバーとして取得する必要があります。

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
これに完全に同意します。ランダムなイベントをテストするには、イベントのランダムな性質を削除します。タイムスタンプにも同じ理論を使用できます
リチャード

3
注:オブジェクトにインスタンスを作成させる代わりに、オブジェクトに必要な他のオブジェクトを与えるというこの手法は、依存性注入
クレメントヘレマン

23

クラスまたはメソッドでランダム生成コードをラップし、テスト中にそれをモック/オーバーライドして、必要な値を設定し、テストを予測できるようにする必要があります。


5

指定された範囲(0〜10)と指定された粒度(整数)があります。そのため、テストするときは、乱数を使用してテストしないでください。各ケースを順番にヒットするループ内でテストします。乱数をcaseステートメントを含むサブ関数に渡すことをお勧めします。これにより、サブ関数をテストすることができます。


私が提案したものよりもはるかに優れている(より単純なため)、私のアップ投票を転送できることを望みます:)
デビッド

実際には両方を行う必要があります。個別に各支店をテストするためのモックRandomObjectとテスト、および実際のRandomObjectと繰り返しテスト。前者は単体テストであり、後者は統合テストに似ています。
sleske

3

PowerMockライブラリを使用してRandomクラスをモックし、その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
     }

    (...)

スイッチでさらにケースを追加する場合は、nextInt(int)呼び出しをスタブして任意のパラメーターを受信することもできます。

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

きれいですね。:)


2

QuickCheckを使用してください!私は最近これで遊んで始めたばかりで、すごいです。ほとんどのクールなアイデアと同様に、Haskellから来ていますが、基本的なアイデアは、事前に用意されたテストケースをテストする代わりに、乱数ジェネレーターで作成できるようにすることです。そうすれば、おそらくxUnitで思いつく4〜6のケースの代わりに、コンピューターに数百または数千の入力を試行させ、どの入力が設定したルールに適合しないかを確認させることができます。

また、QuickCheckは、失敗したケースを見つけると、それを単純化して、失敗する可能性のある最も単純なケースを見つけられるようにします。(そしてもちろん、失敗したケースを見つけたら、それをxUnitテストに組み込むこともできます)

Javaには少なくとも2つのバージョンがあるように見えるため、その部分は問題になりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.