web-dev-qa-db-ja.com

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

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

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

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

展開 David's answer Randomのラッパーを作成する必要があると私は完全に同意します。私はそれについてほぼ同じ答えを先に 同様の質問 で書いたので、ここにその「クリフのノートバージョン」を示します。

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

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
}

お役に立てれば。

22
Spoike

ランダム生成コードをクラスまたはメソッドでラップし、テスト中にモック/オーバーライドして、必要な値を設定して、テストを予測可能にすることができます(推奨)。

23
David

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

5
deworde

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());

きれいですね。 :)

3
LizardCZ

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

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

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

2
Zachary K