だから私は5つのスレッドを開始するメソッドを持っています。5つのスレッドが開始されたことを確認するためだけに単体テストを作成したいと思います。それ、どうやったら出来るの?サンプルコードは大歓迎です。
2 に答える
スレッドを開始するための独自のメソッドを作成する代わりにExecutor
、クラスに挿入できる、を使用してみませんか?次に、ダミーを渡すことで簡単にテストできますExecutor
。
編集:コードを構造化する方法の簡単な例を次に示します。
public class ResultCalculator {
private final ExecutorService pool;
private final List<Future<Integer>> pendingResults;
public ResultCalculator(ExecutorService pool) {
this.pool = pool;
this.pendingResults = new ArrayList<Future<Integer>>();
}
public void startComputation() {
for (int i = 0; i < 5; i++) {
Future<Integer> future = pool.submit(new Robot(i));
pendingResults.add(future);
}
}
public int getFinalResult() throws ExecutionException {
int total = 0;
for (Future<Integer> robotResult : pendingResults) {
total += robotResult.get();
}
return total;
}
}
public class Robot implements Callable<Integer> {
private final int input;
public Robot(int input) {
this.input = input;
}
@Override
public Integer call() {
// Some very long calculation
Thread.sleep(10000);
return input * input;
}
}
そして、これがあなたがあなたからそれを呼ぶ方法ですmain()
:
public static void main(String args) throws Exception {
// Note that the number of threads is now specified here
ExecutorService pool = Executors.newFixedThreadPool(5);
ResultCalculator calc = new ResultCalculator(pool);
try {
calc.startComputation();
// Maybe do something while we're waiting
System.out.printf("Result is: %d\n", calc.getFinalResult());
} finally {
pool.shutdownNow();
}
}
そして、これがあなたがそれをテストする方法です(JUnit 4とMockitoを想定しています):
@Test
@SuppressWarnings("unchecked")
public void testStartComputationAddsRobotsToQueue() {
ExecutorService pool = mock(ExecutorService.class);
Future<Integer> future = mock(Future.class);
when(pool.submit(any(Callable.class)).thenReturn(future);
ResultCalculator calc = new ResultCalculator(pool);
calc.startComputation();
verify(pool, times(5)).submit(any(Callable.class));
}
このコードはすべて、私がまだテストしていないか、コンパイルしようとさえしていないスケッチにすぎないことに注意してください。しかし、それはあなたにコードがどのように構造化されることができるかについての考えを与えるはずです。
「5つのスレッドが開始されたことをテストする」と言うよりも、一歩下がって、5つのスレッドが実際に何をすることになっているのかを考えたほうがよいでしょう。次に、その「何か」が実際に行われていることを確認するためにテストします。
スレッドが開始されたことを本当にテストしたいだけの場合は、いくつかの方法があります。スレッドへの参照をどこかに保持していますか?もしそうなら、あなたは参照を取得し、それらを数え、そしてisAlive()
それぞれを呼び出すことができます(それが戻ることを確認してくださいtrue
)。
実行中のスレッドの数を検索したり、で実行されているすべてのスレッドを検索したりするために呼び出すことができるJavaプラットフォームクラスのメソッドがあると思いますが、ThreadGroup
それが何であるかを検索する必要があります。
あなたのコメントに応えてより多くの考え
コードがのように単純な場合new Thread(runnable).start()
、スレッドが実際に開始されていることをわざわざテストすることはありません。そうする場合、基本的にはJavaプラットフォームが機能することをテストしているだけです(機能します)。スレッドを初期化して開始するためのコードがより複雑な場合は、thread.start()
パーツをスタブして、正しい引数などを使用して、スタブが目的の回数呼び出されるようにします。
それについてあなたが何をするかに関係なく、マルチスレッドモードで実行しているときにタスクが正しく完了することを確実にテストします。個人的な経験から、スレッドでリモートで複雑なことを始めるとすぐに、特定の条件下で、おそらくたまにしか発生しない微妙なバグを簡単に見つけることができます。マルチスレッドコードの複雑さに対処することは、非常に滑りやすい坂道です。
そのため、可能であれば、単純な単体テスト以上のことを行うことを強くお勧めします。マルチコアマシン、非常に大規模なデータセットで、多くのスレッドを使用してタスクを実行するストレステストを実行し、すべての回答が期待どおりであることを確認します。
また、スレッドを使用することでパフォーマンスの向上が期待できますが、プログラムのベンチマークをさまざまなスレッド数でベンチマークして、目的のパフォーマンスの向上が実際に達成されることを確認することを強くお勧めします。システムの設計方法によっては、同時実行のボトルネックが発生する可能性があります。これにより、スレッドを使用しない場合よりもスレッドを使用する場合のプログラムの速度がほとんど低下する可能性があります。場合によっては、さらに遅くなることもあります。