この記事はプログラミング初心者が自分のメモ用として書いている記事です。
そのため、誤った情報が記述されている可能性があります。
自己判断で読み進めてください
スタブ(Stub)とは?
スタブとは、ユニットテストにおいて「他のクラスから呼ばれると、決まった値を返す」だけの簡易的な代用品のこと。
別のクラスやコンポーネントに依存しているロジックを、テスト用に簡易化して置き換える際に使う、
たとえば、毎回値が変わる Random
のような処理を「固定の値で返す」ことで、テストを安定させる目的で使用されるケースで説明する。
サンプルソース解説とその役割
以下は、実際のスタブの構成。
インターフェース RandomNumber
package junit.sample;
public interface RandomNumber {
int nextInt();
}
✅【目的】
- ランダム値を生成するインターフェース。
- 実装を差し替えるための“共通の型”として使う。
本来の実装 RandomNumberImpl
package junit.sample;
import java.util.Random;
public class RandomNumberImpl implements RandomNumber {
private final Random random = new Random();
@Override
public int nextInt() {
return random.nextInt();
}
}
✅【目的】
- 実際のランダム値を返す処理です。
- テストでは「毎回違う結果」になり不安定になる原因です。
✅【このコードの代わりにスタブを使う理由】
- テスト時に
nextInt()
の戻り値が毎回異なると、「期待値がブレる=テスト失敗」になります。
スタブ RandomNumberStub
package junit.sample;
public class RandomNumberStub implements RandomNumber {
@Override
public int nextInt() {
return 0;
}
}
✅【目的】
- 毎回「0」を返す簡易実装。
-
RandomNumberImpl
を「決まった結果にする」ためのスタブ。 - これにより、常に「index 0」の値(例:「A」)が選ばれるようになります。
対象クラス Randoms
package junit.sample;
import java.util.List;
public class Randoms {
RandomNumber random = new RandomNumberImpl();
public <T> T choice(List<T> options) {
if (options.size() == 0) {
return null;
}
int index = random.nextInt() % options.size();
return options.get(index);
}
}
✅【ポイント解説】
-
choice()
メソッドは、与えられたリストからランダムに1件選びます。 -
random
はRandomNumber
型なので、実際にはRandomNumberImpl
かRandomNumberStub
を注入できます。 - ユニットテスト時に外から
random
を差し替えることができる設計になっている点が重要です。
テストクラス RandomsTest
package junit.sample;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
public class RandomsTest {
@Test
void testChoice() {
List<String> options = new ArrayList<>();
options.add("A");
options.add("B");
Randoms sut = new Randoms();
// ラムダ表現のスタブを直接設定
final AtomicBoolean isCalled = new AtomicBoolean(false);
sut.random = new RandomNumber() {
@Override
public int nextInt() {
isCalled.set(true); // 呼び出し確認
return 0; // "A"を選ばせる
}
};
// または sut.random = new RandomNumberStub(); でも可
assertEquals(sut.choice(options), "A");
assertTrue(isCalled.get());
}
}
✅【ポイント解説】
-
sut.random
にスタブ(匿名クラス or RandomNumberStub)を代入 - テストの意図:「Aが選ばれる状況を強制的に作る」
-
AtomicBoolean
により「本当に呼ばれたか」も検証
なぜスタブを使うのか?(現場での観点)
❌ スタブを使わない場合
Randoms sut = new Randoms();
sut.random = new RandomNumberImpl();
-
nextInt()
の返り値が毎回ランダムなため、テスト結果が不安定になります - 例:期待は「A」だったが、
nextInt()
が 1 を返して「B」になり失敗するなど
これは "テストが壊れやすい" 状況となり危険。
✅ スタブを使うと?
- 「常に0を返す」スタブを使えば、100%同じ状態で再現できる
- 意図的に「index 0の選択肢(A)」が選ばれるため、期待値との一致が保証される
- これにより、テストの信頼性を向上させられる。
まとめ
スタブは「決まった値を返す」だけのテスト用の代用クラス |
本物の部分の代わりにスタブを使うと、テストを定義的にできる |
例えば、毎回値が変わるRandom
をスタブで置き換えることで、結果を統一できる |