Testcontainersのコンテナを複数テストクラスで共有して使い回す方法になります。 コンテナを使い回すことで、テストクラスごとにコンテナが起動されテストの実行がめちゃ遅くなってしまう問題を解消することが期待できます。
コンテナをマニュアル起動するクラスを作成する
コンテナを手動起動するようなクラスを抽象クラスとして作ってあげます。
abstract class MySqlContainer { companion object { val mySqlContainer = MySQLContainer<Nothing>("mysql:5.7.32") init { mySqlContainer.start() } } }
テストコード
コンテナを起動するクラスを親クラスに指定することで、コンテナが一度だけ起動するようになります。
Spring Bootのテストでは、@DynamicPropertySource
を使って、親クラスで起動したコンテナの情報をもとにデータベースの接続先情報などを上書きしてあげます。
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) @SpringBootTest @AutoConfigureMockMvc internal class UserHandlerTest( val mockMvc: MockMvc, val dataSource: DataSource ): MySqlContainer() { companion object { @DynamicPropertySource @JvmStatic fun changeProperty(registry: DynamicPropertyRegistry): Unit { val mySqlContainer = MySqlContainer.mySqlContainer registry.add("spring.datasource.url", mySqlContainer::getJdbcUrl) registry.add("spring.datasource.username", mySqlContainer::getUsername) registry.add("spring.datasource.password", mySqlContainer::getPassword) } } val table = Table(dataSource, "user") val changes = Changes(table) changes.setStartPointNow() mockMvc.post("/api/users") { contentType = MediaType.APPLICATION_JSON content = """{"name": "siosio"}""" }.andExpect { status { isOk } } changes.setEndPointNow() Assertions.assertThat(changes) .hasNumberOfChanges(1) .changeOfCreation() .rowAtEndPoint().value("name").isEqualTo("siosio") } }
テストクラスの親クラスに指定できない場合…
すでに親クラスを指定済みで、そこに手を入れられないようなケースにはテストフレームワーク側の拡張機能を使って対応できます。 例えば、JUnit5の場合は以下のような拡張実装を作ることで対応できます。
class MySqlContainer: Extension { companion object { val mySqlContainer = MySQLContainer<Nothing>("mysql:5.7.32") init { mySqlContainer.start() } } }
テストコード側では、@RegisterExtension
で拡張実装を登録し利用できます。
companion object { @JvmField @RegisterExtension val mySqlContainer = MySqlContainer() @DynamicPropertySource @JvmStatic fun changeProperty(registry: DynamicPropertyRegistry): Unit { val mySqlContainer = MySqlContainer.mySqlContainer registry.add("spring.datasource.url", mySqlContainer::getJdbcUrl) registry.add("spring.datasource.username", mySqlContainer::getUsername) registry.add("spring.datasource.password", mySqlContainer::getPassword) } }
注意点
複数テストクラスでコンテナが共有されるので、クリーンな状態でテストが実行できなくなります。
Before
系の処理などで、コンテナの中身をクリーンな状態にしてからテストを行う必要があります。