たとえば、コードが0〜10のランダムなintを生成し、各結果に対して異なる分岐を行う場合、そのようなコードで100%のステートメントカバレッジを保証するテストスイートをどのように設計できますか?
Javaでは、コードは次のようになります。
int i = new Random().nextInt(10);
switch(i)
{
//11 case statements
}
展開 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
}
お役に立てれば。
ランダム生成コードをクラスまたはメソッドでラップし、テスト中にモック/オーバーライドして、必要な値を設定して、テストを予測可能にすることができます(推奨)。
指定された範囲(0〜10)と指定された粒度(整数)があります。したがって、テストするときは、乱数でテストしないでください。各ケースを順番にヒットするループ内でテストします。乱数をcaseステートメントを含むサブ関数に渡すことをお勧めします。これにより、サブ関数をテストできます。
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());
きれいですね。 :)
QuickCheck を使用してください!私は最近これをいじり始めました、そしてそれは素晴らしいです。ほとんどのクールなアイデアと同様に、Haskellに由来しますが、基本的なアイデアは、事前に用意されたテストケースにテストを提供する代わりに、乱数ジェネレーターにビルドさせることです。そうすれば、xUnitで思いつく4-6のケースの代わりに、コンピューターに数百または数千の入力を試行させ、どの入力が設定したルールに準拠していないかを確認できます。
また、QuickCheckは、失敗したケースを見つけると、それを単純化して、失敗する最も単純なケースを見つけられるようにします。 (もちろん、失敗したケースを見つけたら、それをxUnitテストに組み込むこともできます)
Javaには少なくとも2つのバージョンがあるように見えるため、一部に問題はありません。