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つの方法が用意されている。
-
@ExtensionでExtensionを登録する。 -
@RegisterExtensionでプログラマティックにExtensionを登録する。 - ServiceLoaderで自動的に登録する。
今回は1と2の方法でそれぞれExtensionインスタンスの生成がどのタイミングで行われるかを確認する。
確認に用いるExtensionは以下の通り。
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を登録する
ソース
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のfooExtensionのstaticを外してみます。
Lifecycle.PER_METHODの場合
ソース
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回だけインスタンスが生成されるかたちにしてみます。
ソース
+ @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を登録した場合は、当然ながら元のテストクラスのライフサイクルに依存するわけです。
ちゃんと読めば書いてある