現在、すべてのJUnitテストは、@BeforeClass
および@AfterClass
アノテーションでタグ付けされたメソッドを提供する共通の基本クラスから拡張されています。これらすべては、テストで使用する一連の静的リソース/サービスをセットアップするだけです。
これは、いくつかの理由で私には扱いにくいようです。
@BeforeClass
と@AfterClass
が複数回呼び出され、テストの速度が低下します。これらは実際には1回だけ呼び出す必要があります私がしたいのは、現在のBeforeClass/AfterClassロジックを継承チェーンから個別のテストとスイート全体で共有できるものに移動することです。
これは可能ですか?可能であれば、どのようにですか?(問題がある場合、私はJUnit 4.7を使用しています。別のバージョンに更新するのは難しいかもしれません)
最初の問題の解決策は、JUnit 4.9で導入された org.junit.rules.ExternalResource
を介してテストに接続された@ClassRule
の拡張にロジックを移動することです。
public class MyTest {
@ClassRule
public static final TestResources res = new TestResources();
@Test
public void testFoo() {
// test logic here
}
}
public class TestResources extends ExternalResource {
protected void before() {
// Setup logic that used to be in @BeforeClass
}
protected void after() {
// Setup logic that used to be in @AfterClass
}
}
このようにして、以前に基本クラスによって管理されていたリソースは、テストクラス階層から移動され、クラスの実行前に作成し、クラスの実行後に破棄できる、よりモジュール化された/消費可能な「リソース」に移動します。
ただし、両方の問題を同時に解決することについて-つまり、個別のテストの一部として、およびスイートの一部として同じ高レベルのセットアップ/ティアダウンを実行すること-このための特定の組み込みサポートはないようです。 ただし...、自分で実装できます:
@ClassRule
リソースの作成を、内部で参照カウントを行うファクトリパターンに変更して、リソースを作成/破棄するかどうかを決定するだけです。
例(これは大まかなものであり、堅牢性のために微調整/エラー処理が必要になる場合があります):
public class TestResources extends ExternalResource {
private static int refCount = 0;
private static TestResources currentInstance;
public static TestResources getTestResources () {
if (refCount == 0) {
// currentInstance either hasn't been created yet, or after was called on it - create a new one
currentInstance = new TestResources();
}
return currentInstance;
}
private TestResources() {
System.out.println("TestResources construction");
// setup any instance vars
}
protected void before() {
System.out.println("TestResources before");
try {
if (refCount == 0) {
System.out.println("Do actual TestResources init");
}
}
finally {
refCount++;
}
}
protected void after() {
System.out.println("TestResources after");
refCount--;
if (refCount == 0) {
System.out.println("Do actual TestResources destroy");
}
}
}
スイート/テストクラスは両方とも、ファクトリメソッドを通じてリソースを@ClassResource
として使用します。
@RunWith(Suite.class)
@SuiteClasses({FooTest.class, BarTest.class})
public class MySuite {
@ClassRule
public static TestResources res = TestResources.getTestResources();
@BeforeClass
public static void suiteSetup() {
System.out.println("Suite setup");
}
@AfterClass
public static void suiteTeardown() {
System.out.println("Suite teardown");
}
}
public class FooTest {
@ClassRule
public static TestResources res = TestResources.getTestResources();
@Test
public void testFoo() {
System.out.println("testFoo");
}
}
public class BarTest {
@ClassRule
public static TestResources res = TestResources.getTestResources();
@Test
public void testBar() {
System.out.println("testBar");
}
}
個別のテストを実行する場合、再カウントは効果がありません。「実際の初期化」と「実際の破棄」は一度だけ行われます。スイートを実行すると、スイートはTestResourceを作成し、個々のテストはすでにインスタンス化されたものを再利用します(参照カウントにより、スイート内のテスト間で実際に破棄および再作成されなくなります)。
@BeforeClass
および@AfterClass
スイートクラス。
これにより、スイート内のテストクラスが実行される前と、すべてのテストクラスが終了した後に(それぞれ)メソッドが実行されます。
この方法で、それらを一度だけ実行できます。
//..usual @RunWith etc annotations here
public class MySuite{
@BeforeClass
public static void setup(){
}
@AfterClass
public static void tearDown(){
}
}
私は同様の問題に遭遇しました(Springはオプションではなく、MavenプロジェクトでTestSuiteを作成しません)。この問題を解決するために単純なjunitランナーを作成しました。
SharedResourceクラスを記述し、そのリソースを要求するようにテストをマークする必要があります。
public class SampleSharedResource implements SharedResource {
public void initialize() throws Exception {
//init your resource
}
}
@RunWith(JUnitSharedResourceRunner.class)
@JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class})
public class SharedResourceRunnerATest {
...