Integration Test Suiteを持っています。すべてのテストを拡張するためのIntegrationTestBase
クラスがあります。この基本クラスには、API接続とDB接続を確立する@Before
(public void setUp()
)および@After
(public void tearDown()
)メソッドがあります。私がやっていることは、各テストケースでこれらの2つのメソッドをオーバーライドし、super.setUp()
とsuper.tearDown()
を呼び出すだけです。ただし、誰かがスーパーを呼び出すのを忘れたり、間違った場所に置いたりして例外がスローされ、最終的に何かでスーパーを呼び出すのを忘れると、これは問題を引き起こす可能性があります。
私がやりたいのは、基本クラスsetUp
にtearDown
およびfinal
メソッドを作成し、独自の注釈付き@Before
および@After
メソッドを追加することです。いくつかの初期テストを行うと、常にこの順序で呼び出されるように見えます:
Base @Before
Test @Before
Test
Test @After
Base @After
しかし、順序が保証されておらず、問題が発生する可能性があることを少し心配しています。私は周りを見回したが、この件については何も見ていません。私がそれを行うことができ、問題がないかどうかは誰にもわかりますか?
コード:
public class IntegrationTestBase {
@Before
public final void setUp() { *always called 1st?* }
@After
public final void tearDown() { *always called last?* }
}
public class MyTest extends IntegrationTestBase {
@Before
public final void before() { *always called 2nd?* }
@Test
public void test() { *always called 3rd?* }
@After
public final void after() { *always called 4th?* }
}
以前に私を噛んだことのある潜在的な落とし穴:
クラス内で定義された@Before
メソッドの実行順序が保証されていないため、各テストクラスに最大で1つの@Before
メソッドが必要です。通常、このようなメソッドsetUpTest()
を呼び出します。
ただし、@Before
はThe @Before methods of superclasses will be run before those of the current class. No other ordering is defined.
として文書化されていますが、@Before
でマークされた各メソッドがクラス階層で一意の名前を持っている場合にのみ適用されます。
たとえば、次のものがありました。
public class AbstractFooTest {
@Before
public void setUpTest() {
...
}
}
public void FooTest extends AbstractFooTest {
@Before
public void setUpTest() {
...
}
}
AbstractFooTest.setUpTest()
の前にFooTest.setUpTest()
が実行されると予想しましたが、FooTest.setupTest()
のみが実行されました。 AbstractFooTest.setUpTest()
はまったく呼び出されませんでした。
コードを機能させるには、次のように変更する必要があります。
public void FooTest extends AbstractFooTest {
@Before
public void setUpTest() {
super.setUpTest();
...
}
}
@Before
および@After
のドキュメントに基づいて、正しい結論はメソッドに一意の名前を付けることだと思います。テストでは次のパターンを使用します。
public abstract class AbstractBaseTest {
@Before
public final void baseSetUp() { // or any other meaningful name
System.out.println("AbstractBaseTest.setUp");
}
@After
public final void baseTearDown() { // or any other meaningful name
System.out.println("AbstractBaseTest.tearDown");
}
}
そして
public class Test extends AbstractBaseTest {
@Before
public void setUp() {
System.out.println("Test.setUp");
}
@After
public void tearDown() {
System.out.println("Test.tearDown");
}
@Test
public void test1() throws Exception {
System.out.println("test1");
}
@Test
public void test2() throws Exception {
System.out.println("test2");
}
}
結果として与える
AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown
このアプローチの利点:AbstractBaseTestクラスのユーザーは、誤ってsetUp/tearDownメソッドをオーバーライドできません。希望する場合は、正確な名前を知っている必要があります。
(軽微な)このアプローチの欠点:ユーザーは、setUp/tearDownの前後に発生していることを確認できません。これらは、抽象クラスによって提供されることを知る必要があります。しかし、それが抽象クラスを使用する理由だと思います
@BeforeClass
アノテーションを使用して、setup()
が常に最初に呼び出されるようにすることができます。同様に、@AfterClass
注釈を使用して、tearDown()
が常に最後に呼び出されるようにすることができます。
通常、これは推奨されませんが、 サポート です。
望みどおりではありませんが、テストの実行中は基本的にDB接続を開いたままにし、最後に一度だけ閉じます。
これは、キャッチフレーズの質問に対する答えではありませんが、質問の本文で言及されている問題に対する答えです。 @Beforeまたは@Afterを使用する代わりに、 @ org.junit.Rule の使用を検討してください。これにより、柔軟性が向上します。 ExternalResource (4.7現在)は、接続を管理する場合に最も関心のあるルールです。また、ルールの実行順序を保証したい場合は、 RuleChain (4.10以降)を使用します。この質問が尋ねられたとき、これらはすべて利用可能であったと思います。以下のコード例は、ExternalResourceのjavadocsからコピーされています。
public static class UsesExternalResource {
Server myServer= new Server();
@Rule
public ExternalResource resource= new ExternalResource() {
@Override
protected void before() throws Throwable {
myServer.connect();
};
@Override
protected void after() {
myServer.disconnect();
};
};
@Test
public void testFoo() {
new Client().run(myServer);
}
}
物事を好転させる場合は、基本クラスの抽象を宣言し、子孫に基本クラスの注釈付きsetUpおよびtearDownメソッドで呼び出されるsetUpおよびtearDownメソッド(注釈なし)を宣言させることができます。