JUnit-セットアップメソッドを1回実行する


119

いくつかのテストを使用してクラスを@Beforeセットアップしました。使用するのではなく、すべてのテストの前に1回だけ実行されるsetupメソッドを使用したいと考えています。JUnit 4.8でそれは可能ですか?


1
RunListenerをご覧ください:stackoverflow.com/a/14773170/548473
Grigory Kislin

回答:


205

@assyliasに同意しますが、使用@BeforeClassは古典的な解決策であり、常に便利であるとは限りません。アノテーションが付けられたメソッドは@BeforeClass静的でなければなりません。テストケースのインスタンスを必要とする一部のテストでは、非常に不便です。たとえば、Spring @Autowiredコンテキストで定義されたサービスを操作するために使用するSpringベースのテスト。

この場合、私はsetUp()注釈付きの通常のメソッドを個人的に使用し@Before、カスタムstatic(!) booleanフラグを管理します。

private static boolean setUpIsDone = false;
.....
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}

10
なぜ静的でなければならないかについてのケニー・ケイソンのコメントに追加。JUnitは@Testメソッドごとにテストクラスの新しいインスタンスをインスタンス化するため、静的でなければなりません。インスタンス変数が静的でない場合、インスタンス変数はインスタンスごとにデフォルト値(false)にリセットされます。詳細については、martinfowler.com
bliki

2
これは、setUp()メソッドがスーパークラスにある場合を除いて機能します- これを解決するために以下の回答を投稿しました。
Steve Chambers

4
84kの担当者にこれを言うのをためらいますが、BeforeClassは実際には質問に答えません。BeforeClassはすべてのテストクラスの最初に実行されます。しかし、OPは「すべてのテストの前に1回だけ」実行するものを求めました。提案されたソリューションはこれを行うことができますが、すべてのテストクラスに「CommonTest」クラスを拡張させる必要があります...
マイク齧歯類

1
@mikerodent、私見OPは、テストケース全体のすべてのテストではなく、テストケース内のすべてのテストについて尋ねました。したがって、コメントの関連性は低くなります。ところで、たとえ彼の評判が高くても、誰にも何も言わないでください。少なくともこれは私がすることです:)。そして、私の質問に答えた2012年8月の時点で、私の評判は大幅に低下しました。
AlexR

私の場合は機能しません。セットアップで初期化された変数は各テストの後にリセットされるため、1回だけ初期化しても意味がありません。
Aphax 2017年

89

あなたは使用することができ、注釈をBeforeClass

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}

12
私はこれを使用できません。getClass()などの非静的コンポーネントに基づくセットアップメソッドがいくつかあります
Bober02

1
@ Bober02 BeforeClassは確かに静的である必要があります。それを使用できない場合は、他の回答で回避策を提供します。
assylias 2012

2
TheClassYouWant.classgetClass()コールの代わりに使用できないことを確認しますか?これは実際のJavaですString.class.getName()
stolsvik 2013


1
@mikerodent私は「クラスのすべてのテスト」として質問を理解しました-しかし、あなたは正しいです、それはOPが望んだものではないかもしれません。
アッシリア

29

JUnit 5に@BeforeAllアノテーションが追加されました。

注釈付きメソッドは、現在のクラスまたはクラス階層内のすべての@Testメソッドの前に実行する必要があることを示します。JUnit 4の@BeforeClassに似ています。そのようなメソッドは静的でなければなりません。

JUnit 5のライフサイクルアノテーションがついに正しくなったようです!見ていなくても使用可能な注釈を推測できます(例:@BeforeEach @AfterAll)


6
それは同じ問題があった@BeforeClass、それはする必要がありますstatic。IMO @AlexRのソリューションの方が優れています。
zengr 16

@zengrはあなたに同意する傾向があります。私がAlexRに言ったように、彼のソリューションでは、すべてのテストクラスが1回だけ実行される場合、CommonTestクラスからサブクラス化する必要があります。しかし、それは単純なことと同じくらい簡単であり、IMHOはおそらく、言語から単純なメカニズムを利用できる場合は、「ファンシー」フレームワーク提供のソリューションを使用すべきではありません。もちろん正当な理由がない限り。また、彼のような単純なものを使用し、「缶に書かれていることをする」という良いタイプ名を使用すると、読みやすくなります。
マイクげっ歯類2017年

これについても、私見では、「AfterAll」注釈を付けることの正当性ははるかに高くなります。すべてのテストが完了したときを検出するメカニズムを考案することは非常に困難であり、不自然です。逆に、もちろん、純粋主義者は、「最終的なクリーンアップ」を行う必要がないと言うつもりです。つまり、各「tearDown」は、すべてのリソースを元の状態のままにする必要があります...そして、それらはおそらく正しいでしょう!
マイクげっ歯類2017年

これは複数のモジュールがあり、それぞれにテストがあるMavenで動作しますか?
マークブーン

@mikeげっ歯類、私の場合、各テストの前後にファイルシステムでテストファイルをセットアップして破棄すると、ファイルのデッドロックが発生するようです。今のところ、私はAlexRのソリューションを1回セットアップするために独立して到着しました。2つの静的フラグ、alreadySetupとdirtyがあります。setup()は、最初にダーティ状態が検出された場合、またはセットアップの失敗によりダーティ状態になった場合に、cleanup()を呼び出します。テストの実行後にクリーンアップするために、もう一度実行します。乱雑で、理想的ではなく、ビルドプロセスではありません。まだより良い方法を探しています(jUnit 4.12)。
レベッカ2017

9

ときにsetUp()テストクラスのスーパークラスである(例えばAbstractTestBase、以下のように以下)、受け入れ答えを変更することができます。

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

これは単一の非静的setUp()メソッドで機能するはずですがtearDown()、複雑な反射の世界に迷わずに相当するものを作成することはできません...


3

編集: デバッグ中に、すべてのテストの前にもクラスがインスタンス化されることがわかりました。ここでは@BeforeClassアノテーションが最適だと思います。

コンストラクターでもセットアップできます。テストクラス結局のところクラスです。他のほとんどすべてのメソッドに注釈が付けられているため、これが悪い習慣かどうかはわかりませんが、機能します。次のようなコンストラクタを作成できます。

public UT () {
    // initialize once here
}
@Test
// Some test here...

ctorは静的ではないため、テストの前に呼び出されます。



0

私の汚い解決策は:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

私はすべてのテストケースのベースベースとして使用しています。


パブリッククラスTestCaseExtended extends TestCase {private static boolean isInitialized = false; プライベート静的TestCaseExtended caseExtended; private int serId; @Override public void setUp()throws Exception {super.setUp(); if(!isInitialized){caseExtended = new TestCaseExtended(); caseExtended.loadSaveNewSerId(); caseExtended.emptyTestResultsDirectory(); isInitialized = true; }}
Obi Two

0

各サブテストで設定およびチェックされる変数の宣言を強制したくない場合は、これをSuperTestに追加すると次のようになります。

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}

0

私はこの問題を次のように解決しました:

ベース抽象クラス(つまりsetUpDriver()メソッドでドライバーを初期化する抽象クラス)にコードの次の部分を追加します。

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

そして今、あなたのテストクラスであれば意志から拡張する基本抽象クラス- > setUpDriver()メソッドが@Test最初の前にのみ実行されますONE実行当たりの時間を。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.