JUnitの@ Before / @ Afterはどのような順序で呼び出されますか?


133

統合テストスイートがあります。IntegrationTestBaseすべてのテストを拡張するためのクラスがあります。この基本クラスには、APIおよびDB接続を確立するための@Beforepublic void setUp())および@Afterpublic void tearDown())メソッドがあります。私が行ってきたことは、単に各テストケースでは、これら2つのメソッドをオーバーライドして呼んでいるsuper.setUp()super.tearDown()。ただし、誰かがスーパーを呼び出すのを忘れたり、間違った場所に置いたりして例外がスローされ、最後に何かをスーパーを呼び出すのを忘れた場合、これは問題を引き起こす可能性があります。

私は何をしたい作りであるsetUptearDown、基本クラスのメソッドを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?* }
}

1
されてMyTest行方不明extends
aioobe 2013年

@aioobe:もうありません:)
Joel

回答:


135

はい、この動作は保証されています:

@Before

@Beforeスーパークラスのメソッドは、現在のクラスでオーバーライドされない限り、現在のクラスのメソッドの前に実行されます。他の順序は定義されていません。

@After

@Afterスーパークラスで宣言されたメソッドは、現在のクラスでオーバーライドされない限り、現在のクラスのメソッドの後に実行されます。


15
明確にするために、すべての@Beforeメソッドの実行順序は保証されていません。10個の@Beforeメソッドがある場合、それぞれのメソッドは任意の順序で実行できます。他のメソッドの直前。
Swati

5
ややあいまいなドキュメントを引用する代わりに、あなた自身の言葉で説明していただけませんか?メソッドは@Before、他のすべてのクラスメソッドの@After(メソッドごとに1回)、またはクラスメソッドのスイート全体の直前と直後(クラスごとに1回)に実行されますか?
BT

5
John Q Citizenによる重要なキャッチを参照してください。「これは、@ Beforeでマークされた各メソッドがクラス階層で一意の名前を持っている場合にのみ適用されます」覚えておくことが非常に重要です!
Bruno Bossola 2014

クラスの@Before(d)メソッドとそのスーパークラスの別のメソッドで同じメソッド名を使用して、名前の競合が発生しましたjunit-4.12
ステファン

このルールは、ConcordionRunnerの@BeforeExampleメソッドにも適用されますか?
Adrian Pronk

51

以前私を悩ませた1つの潜在的な落とし穴:

@Beforeクラス@Before内で定義されたメソッドの実行順序は保証されていないため、各テストクラスに最大で1つのメソッドを含めるのが好きです。通常、このようなメソッドを呼び出します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メソッドの名前を変更しないのはなぜですか?これにより、すべての子供たちがスーパーに電話する必要がなくなります。とにかく、同じ名前の問題で良いキャッチ
Lawrence Tierney 2014年

24
物事をより安全にするための単なる注釈:名前の衝突を回避するために、基本クラスで@Before/ @Afterメソッドを作成できるfinalので、(誤って)サブクラスでそれらをオーバーライドしようとするとコンパイラーが文句を言います。
Stefan Winkler

4
実行されていない同じ名前の親メソッドは、JUnitの動作のように聞こえません。これは、OOPでの基本的な上書きのしくみに似ています。親メソッドは基本的に実行時には存在しません。子供はすべての意図と目的のためにそれを交換します。これがJavaの仕組みです。
ブランドン

1
もう1つの問題は、親クラスがパブリックでなければならないことです。それ以外の@Before場合、サブクラスにもメソッドがある場合、マークされたメソッドは無視され@Beforeます。
rusins

21

のドキュメントに基づく@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の前または後に何かが起こっていることを認識できません。彼らはこれらのものが抽象クラスによって提供されることを知る必要があります。しかし、それが彼らが抽象クラスを使用する理由だと思います


2
良い例-@Testメソッドが2つある場合はさらにわかりやすくなるので、setUpとtearDownがテストメソッドをラップしていることがわかります。
マーク

これがOPに対する最良の回答の基礎であると思いますが、スタンドアロンへの回答を記入する必要があります。他の人が提案した代替案をカバーするように例を補強し、提案が優れている理由を説明できますか?
wachr 2017

2

状況を変えると、基本クラスの抽象を宣言し、子孫に基本クラスの注釈付きsetUpメソッドとtearDownメソッドで呼び出されるsetUpメソッドとtearDownメソッド(注釈なし)を宣言させることができます。


1
悪い考えではありませんが、独自のsetUp / tearDownを必要としないテストで契約を強制したくありません
Joel

2

@BeforeClassアノテーションを使用してsetup()、常に最初に呼び出されるようにすることができます。同様に、@AfterClassアノテーションを使用して、tearDown()常に最後に呼び出されるようにすることができます。

これは通常は推奨されませんが、サポートされています。

これは必ずしも望ましいことではありませんが、テストが実行されている間は基本的にDB接続を開いたままにして、最後に一度だけ閉じます。


2
あなたがこれを行うにした場合、実際に、私は、メソッドを作成することをお勧めしたいsetupDB()closeDB()して、それらをマーキング@BeforeClassし、@AfterClassそして持つメソッドの後に、あなたの前に/を交換setup()してtearDown()
スワティ

アノテーションが付けられ@BeforeClass@AfterClass静的である必要があるメソッド。これらのメソッド内でインスタンス変数を使用したい場合はどうでしょうか?
Pratik Singhal

@BeforeClassPowermockで使用する場合の警告:最初のテスト実行でのみ機能します。この問題を参照してください:github.com/powermock/powermock/issues/398
Dagmar

2

これはキャッチコピーの質問への回答ではありませんが、質問の本文で言及されている問題への回答です。@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);
     }
 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.