@BeforeClassと継承-実行順序


90

単体テスト(TestNG 5.10)のベースとして使用する抽象基本クラスがあります。このクラスでは、テスト用の環境全体を初期化したり、データベースマッピングを設定したりしています。この抽象クラス@BeforeClassには、初期化を行う注釈付きのメソッドがあります。

次に、そのクラスを、@Testメソッドとメソッドを持つ特定のクラスで拡張し@BeforeClassます。これらのメソッドは、環境のクラス固有の初期化を行います(たとえば、いくつかのレコードをデータベースに入れます)。

@BeforeClassアノテーション付きメソッドの特定の順序を強制するにはどうすればよいですか?抽象基本クラスからのものを拡張クラスの前に実行する必要があります。

例:

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @BeforeClass
    doSpecificInitialization() {...}

    @Test
    doTests() {...}
}

予想される注文:

A.doInitialization
B.doSpecificInitialization
B.doTests

実際の注文:

B.doSpecificInitialization // <- crashes, as the base init is missing
(A.doInitialization        // <---not executed
 B.doTests)                // <-/

回答:


50

入れないでください@BeforeClass上のabstractクラス。各サブクラスから呼び出します。

abstract class A {
    void doInitialization() {}
}

class B extends A {
    @BeforeClass
    void doSpecificInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

TestNGのようです@BeforeClass(dependsOnMethods={"doInitialization"})-試してみてください。


9
それは基本的に私が避けたかったことです。スーパー(抽象)クラスのメソッドを明示的に呼び出す必要はありません。特に、Aから継承するクラスがありますが、独自の@BeforeClassメソッドはありません。その目的のためだけに挿入する必要があります。
ドミニクサンドジャジャ2010年

5
dependsOnMethodsこの問題を回避するには、トリックを行いました。私は「スーパークラスファースト」のアプローチを好むが…
ドミニクサンドジャジャ

1
「dependsOnMethod」を使用するには、「doInitialization」に「@Test」の注釈を付けないでください。技術的にそれは...それだけでテストではありませんので、それは問題である
N3da

@BeforeClassは静的メソッドに注釈を付ける必要があります
Fabrizio

108

編集:以下の答えはJUnit用ですが、役立つので、とにかくここに残します。

JUnit apiによれば、「スーパークラスの@BeforeClassメソッドは、現在のクラスの前に実行されます。」

私はこれをテストしました、そしてそれは私のために働くようです。

ただし、@ Odysで後述するように、JUnitでは2つのメソッドに異なる名前を付ける必要があります。そうしないと、親がシャドウされるため、サブクラスメソッドのみが実行されます。


56
元の質問はTestNGに関するものでしたが、JUnitをグーグル検索した後、私はここに到着しました。あなたの答えは役に立ちました-ありがとう!
ティーボット、2011年

9
JUnitの場合、2つのメソッドに異なる名前を付ける必要があります。ただし、そうしないと、親がシャドウされるため、サブクラスメソッドのみが実行されます。
Odys

2
@Odys、これについて言及していただきありがとうございます。スーパークラスのメソッドが実行されていないのに、サブクラスの「セットアップ」メソッドが実行されていた理由を理解するのに苦労していました。あなたは私にひどい重荷を救った!
Tom Catullo 2017

あなたは私の日を作りました。ありがとう!
レイク

7

public抽象クラスに追加して、TestNG(6.0.1)がdoInitialization()を実行しましたdoTests。クラスAからdoInitialization()削除すると、TestNGが実行されませんpublic

public abstract class A {
 @BeforeClass
 doInitialization() {...}
}

class B extends A {    
 @Test
 doTests() {...}
}

1
それは本当ですが、無関係です。クラスは、とき、これは動作しません。B また、持っている@BeforeClassOPの場合のように、-annotated方法を。
jpaugh

1
私も同じことをしました。基本メソッドがプライベートだと継承の順番が抜けているようです。ありがとう!
Manu

6

5.11で例を試したところ、最初に基本クラスの@BeforeClassが呼び出されました。

testng.xmlファイルを投稿できますか?AとBの両方を指定しているかもしれませんが、必要なのはBだけです。

testng-usersメーリングリストをフォローしてください。問題を詳しく調査いたします。

-セドリック


2
testngの.xmlは(明示的に)定義されていません。EclipseとMavenから実行されます。
ドミニクサンドジャジャ2010年

Eclipseから正確にどのように実行していますか?クラスBを右クリックしますか?
Cedric Beust

4

私はこれを経験し、これを達成するもう1つの方法を見つけました。抽象クラスalwaysRun@BeforeClassまたはその中で使用するだけ@BeforeMethodで、期待どおりに機能します。

public class AbstractTestClass {
    @BeforeClass(alwaysRun = true)
    public void generalBeforeClass() {
        // do stuff
        specificBeforeClass();
    }
}

2

私が実行するとき:JUnitCore.runClasses(TestClass.class); 親が子の 前に正しく実行されます(super.SetUpBeforeClass();は必要ありません)。Eclipseから実行する場合:何らかの理由で基本クラスの実行に失敗します。回避策:基本クラスを明示的に呼び出します:(BaseTest.setUpBeforeClass();)アプリケーションから実行する場合に備えて、基本クラスにフラグを設定して、既に設定されているかどうかを確認することができます。したがって、両方の可能な方法(個人テストの場合はeclipseから、ビルドリリースの場合はANTを介して)で実行した場合にのみ、1回だけ実行されます。

これはEclipseのバグ、または少なくとも予期しない結果のようです。


2

JUnitの場合:@fortegaが言及したように:JUnit apiによれば、「スーパークラスの@BeforeClassメソッドは、現在のクラスの前に実行されます。」

ただし、両方のメソッドに同じ名前を付けないように注意してください。この場合、親メソッドは子親によって非表示になるためです。ソース


1

@BeforeClassメソッドで空のspecificBeforeClass()メソッドを呼び出して、次のようなサブクラスで上書きされる場合とされない場合はどうでしょうか。

public class AbstractTestClass {
  @BeforeClass
  public void generalBeforeClass() {
    // do stuff
    specificBeforeClass();
  }

  protected void specificBeforeClass() {}
}

public class SpecificTest {
  @Override
  protected void specificBeforeClass() {
    // Do specific stuff
  }

  // Tests
}


1

dependsOnMethod に使える。

例:春(AbstractTestNGSpringContextTests)の場合

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")

1

インポート文を確認してください。そのはず

import org.testng.annotations.BeforeClass;

ない

import org.junit.BeforeClass;


1

これは私にとってはうまくいきます-

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @Override
    @BeforeClass
    doInitialization() { 

       //do class specific init

    }   

    @Test
    doTests() {...}
}

1
このコードの機能の簡単な説明を追加し、元の質問に答えます
Cray

0

スーパークラスのBeforeClassアノテーション付きメソッドから呼び出される、スーパークラスで抽象メソッドdoSpecialInit()を作成してみませんか。

したがって、クラスを継承する開発者は、このメソッドを実装する必要があります。


正直に言うと、私がこの質問をした後、ロジックは過去3年半以内に変更されたかもしれません... ;-)はい、多分これはアイデアだったかもしれません。覚えておいてください。
ドミニクサンジャジャ2013

0

ここに別の簡単な解決策があります。

私の特別な状況では、スーパークラスの「BeforeClass」が実行される前に、サブクラスの「BeforeClass」からモックサービスを注入する必要があります。

これを行うに@ClassRuleは、サブクラスで単にa を使用します。

例えば:

@ClassRule
public static ExternalResource mocksInjector = new ExternalResource() {
    @Override
    protected void before() {
        // inject my mock services here
        // Note: this is executed before the parent class @BeforeClass
    }
};

これがお役に立てば幸いです。これにより、「逆」の順序で静的セットアップを効果的に実行できます。


0

私は今日同様の問題に直面しました、唯一の違いは基本クラスが抽象的ではなかったことです

これが私のケースです

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    private void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

@BeforeClassクラスAのメソッドが実行されなかったことが発生しました。

  • A.doInitialization()->これはサイレント実行されたことはありません
  • B.doSpecificInitialization()
  • B.doTests()

プライバシー修飾子で遊んでいると、メソッドがクラス継承者から見えない場合、 TestNG @BeforeClass継承されたクラスの注釈付きメソッドを実行ないことがわかりました

だからこれはうまくいくでしょう:

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    //Here a privacy modifier matters -> please make sure your method is public or protected so it will be visible for ancestors
    protected void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

その結果、次のことが起こります。

  • A.doInitialization()
  • B.doSpecificInitialization()
  • B.doTests()

-1

私のケース(JUnit)では、基本クラスと派生クラスにsetup()と同じメソッドがあります。この場合、派生クラスのメソッドのみが呼び出され、基本クラスメソッドを呼び出します。


-2

継承を使用してこれを達成するためのより良い、よりクリーンな方法は次のようになるかもしれません-

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

    @Override
    @BeforeClass
    void doInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

1
Parentクラスのメソッドを最初に実行する必要がある場合は、ChildクラスのメソッドにParentとは異なる名前を付ける必要があります(同じシグネチャを持つ場合、ポリモーフィズムが動作するため)。私はそれがよりクリーンな方法だと信じています。
Anatolii Stepaniuk 16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.