JUnit 5 × Kotlinで@BeforeAll
や@AfterAll
を使いたかったり、Parameterized Testで@MethodSource
用のstatic相当なメソッドを宣言したかったりするときなどは、対象のクラスに@TestInstance(Lifecycle.PER_CLASS)
を指定します。
このアノテーションを指定したクラスでのモックオブジェクトの初期化処理でハマったので、その内容を備忘録として残しておきます。
公式ドキュメントに全て書いてありますが、@TestInstance(Lifecycle.PER_CLASS)
を指定したクラスは、テスト時にインスタンスの生成が1回しか行われないので、スタブメソッドの設定を別のメソッド内で行う必要がありました。
Lifecycle.PER_CLASS
- When using this mode, a new test instance will be created once per test class.
Lifecycle.PER_METHOD
(デフォルト)- When using this mode, a new test instance will be created for each test method, test factory method, or test template method.
例えば、MockKを使ってContextオブジェクトをモックしたいときは、以下のように書きます。
@TestInstance(Lifecycle.PER_CLASS) class SampleTest { // モックオブジェクトの生成だけ最初に行う private val mockContext: Context = mockk() @BeforeAll fun beforeAll() { mockkStatic(/** 必要に応じて設定 **/) mockkObject(/** 必要に応じて設定 **/) } @BeforeEach fun beforeEach() { // 共通で使うスタブの振る舞いをセットする mockContext.run { every { getString(any(), *anyVararg()) } returns "sample" } } @AfterEach fun afterEach() { clearAllMocks() } @AfterAll fun afterAll() { unmockkAll() } @Test fun hogeTest() { // mockContextを利用した処理を行う } @Test fun fugaTest() { // mockContextを利用した処理を行う } }
これを以下のように書いてしまったため、2回目にmockContextを使うときに期待通りの結果が返ってこなくなっていました。
@TestInstance(Lifecycle.PER_CLASS) class SampleTest { // モックオブジェクトの生成と共通で使うスタブの振る舞いをセットしていた private val mockContext: Context = mockk { every { getString(any(), *anyVararg()) } returns "sample" } @BeforeAll fun beforeAll() { mockkStatic(/** 必要に応じて設定 **/) mockkObject(/** 必要に応じて設定 **/) } @BeforeEach fun beforeEach() { // ここでスタブの振る舞いをセットしていなかった } @AfterEach fun afterEach() { // ここでmockContextの情報がリセットされてしまった clearAllMocks() } @AfterAll fun afterAll() { unmockkAll() } @Test fun hogeTest() { // mockContextを利用した処理を行う } @Test fun fugaTest() { // mockContextを利用した処理を行う } }
私のケースでは、元々期待通りに動いていたテストクラスに@MethodSource
を使ったParameterized Testを追加するために@TestInstance(Lifecycle.PER_CLASS)
を指定したときに、この事象に遭遇しました。
今まで機械的に@TestInstance(Lifecycle.PER_CLASS)
を使っていたので、理解を深めるいい機会となりました。