3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JUnit JupiterのExtensionのライフサイクルを調べてみた

Posted at

JUnit 5(JUnit Jupiter)のExtensionを書こうとしてExtensionインスタンスのライフサイクルってどうなってるんだっけ?となったので備忘録的にメモ。

環境

  • AdoptOpenJDK 11
  • JUnit 5.5.0

TL;TR

  • @Extensionで登録したインスタンスはテストクラスごとに共有され、テストクラスが異なればExtensionインスタンスも異なる。
  • @RegisterExtensionで登録したインスタンス
    • staticフィールドの場合はテストクラスごとに共有される。
    • non-staticフィールドの場合はテストクラスのライフサイクルに依存する。

Extension Modelのおさらい

JUnit 5のExtension Modelは各テストに対する前処理・後処理・例外処理などを共通化するための仕組み。Junit 4のRuleなどに比べて自由度は下がるが、拡張ポイントに対応したインタフェースを実装するだけで手軽にExtensionを作成できる。

Extensionを使うには3つの方法が用意されている。

  1. @ExtensionでExtensionを登録する。
  2. @RegisterExtensionでプログラマティックにExtensionを登録する。
  3. ServiceLoaderで自動的に登録する。

今回は1と2の方法でそれぞれExtensionインスタンスの生成がどのタイミングで行われるかを確認する。

確認に用いるExtensionは以下の通り。

FooExtension.java
import org.junit.jupiter.api.extension.*;

public class FooExtension implements BeforeEachCallback, AfterEachCallback, BeforeAllCallback, AfterAllCallback {

    {
        System.out.println(String.format("[%s] created", this.toString()));
    }

    @Override
    public void beforeAll(ExtensionContext extensionContext) throws Exception {
        System.out.println(String.format("[%s] before all", this.toString()));
    }

    @Override
    public void beforeEach(ExtensionContext context) throws Exception {
        System.out.println(String.format("[%s] before each", this.toString()));
    }

    @Override
    public void afterEach(ExtensionContext extensionContext) throws Exception {
        System.out.println(String.format("[%s] after each", this.toString()));
    }

    @Override
    public void afterAll(ExtensionContext extensionContext) throws Exception {
        System.out.println(String.format("[%s] after all", this.toString()));
    }
}

@ExtensionでExtensionを登録する

ソース

@ExtendWith(FooExtension.class)
public class ExtensionTest {

    @Test
    void test1() { System.out.println("test 1"); }

    @Test
    void test2() { System.out.println("test 2"); }

    @Test
    void test3() { System.out.println("test 3"); }
}

結果

[example.FooExtension@70325e14] created
[example.FooExtension@70325e14] before all
[example.FooExtension@70325e14] before each
test 1
[example.FooExtension@70325e14] after each
[example.FooExtension@70325e14] before each
test 2
[example.FooExtension@70325e14] after each
[example.FooExtension@70325e14] before each
test 3
[example.FooExtension@70325e14] after each
[example.FooExtension@70325e14] after all

各テストで1つExtensionのインタンスが共有されていることがわかります。
なお、ExtensionTestはテストごとにインスタンスが生成されますが、@TestInstance(Lifecycle.PER_CLASS)をつけてExtensionTestのインスタンスも共有するように変更した場合も同じ結果になりました。

@RegisterExtension + staticフィールドでExtensionを登録する

ソース

SecondExtensionTest.java
public class SecondExtensionTest {

    @RegisterExtension
    static FooExtension fooExtension = new FooExtension();

    @Test
    void test1() { System.out.println("test 1"); }

    @Test
    void test2() { System.out.println("test 2"); }

    @Test
    void test3() { System.out.println("test 3"); }
}

結果

[example.FooExtension@55183b20] created
[example.FooExtension@55183b20] before all
[example.FooExtension@55183b20] before each
test 1
[example.FooExtension@55183b20] after each
[example.FooExtension@55183b20] before each
test 2
[example.FooExtension@55183b20] after each
[example.FooExtension@55183b20] before each
test 3
[example.FooExtension@55183b20] after each
[example.FooExtension@55183b20] after all

当然ですが、staticフィールドなので同じインスタンスが共有されます。

@RegisterExtension + non-staticフィールドでExtensionを登録する

先程のSecondExtensionTestのfooExtensionstaticを外してみます。

Lifecycle.PER_METHODの場合

ソース

SecondExtensionTest.java
public class SecondExtensionTest {

    @RegisterExtension
-   static FooExtension fooExtension = new FooExtension();
+   FooExtension fooExtension = new FooExtension();

    //後略
}

結果

[example.FooExtension@40a4337a] created
[example.FooExtension@40a4337a] before each
test 1
[example.FooExtension@40a4337a] after each
[example.FooExtension@fa4c865] created
[example.FooExtension@fa4c865] before each
test 2
[example.FooExtension@fa4c865] after each
[example.FooExtension@3bd82cf5] created
[example.FooExtension@3bd82cf5] before each
test 3
[example.FooExtension@3bd82cf5] after each

テストごとにExtensionのインスタンスが異なっていることが分かります。これはSecondExtensionTest自体のインスタンスがテストごとに毎回生成されていることが原因です。
なお、BeforeAllとAfterAllが動いていませんが、メソッドに@BeforeAllをつけるときと同様Lifecycle.PER_METHODの場合はstaticにしなければいけません。

Lifecycle.PER_CLASSの場合

ということでSecondExtensionTestのライフサイクルを、1回だけインスタンスが生成されるかたちにしてみます。

ソース

SecondExtensionTest.java
+ @TestInstance(Lifecycle.PER_CLASS)
public class SecondExtensionTest {

    @RegisterExtension
    FooExtension fooExtension = new FooExtension();

    //後略
}

結果

[example.FooExtension@c730b35] created
[example.FooExtension@c730b35] before all
[example.FooExtension@c730b35] before each
test 1
[example.FooExtension@c730b35] after each
[example.FooExtension@c730b35] before each
test 2
[example.FooExtension@c730b35] after each
[example.FooExtension@c730b35] before each
test 3
[example.FooExtension@c730b35] after each
[example.FooExtension@c730b35] after all

Extensionインスタンスが共有されています。また、BeforeAllとAfterAllが動くようになりました。このようにして、non-staticフィールドでExtensionを登録した場合は、当然ながら元のテストクラスのライフサイクルに依存するわけです。

ちゃんと読めば書いてある

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?