複数のテストクラスの共通の前処理、後処理を作成したい場合、JUnit の ExternalResource クラスを継承したクラスを用意すれば実現できますが、ExternalResource クラスを継承したクラスの中で Spring Boot の Java Configuration のクラスで定義した DataSource Bean をインジェクションして使いたいと思い、調査に結構時間がかかったのでそのメモ書きです。
例えば Spring Boot の Webアプリケーションの中で以下のように Java Configuration のクラスで DataSource Bean を定義しているものとします。
@Configuration
public class ApplicationConfig {
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
テストクラスの共通処理として ExternalResource クラスを継承したクラスを作成し、その中で以下のように DataSource を @Autowired アノテーションを付加して定義した後、テストクラスの方では TestDataResource を @Rule アノテーションを付加して定義したのですが、テストを実行すると TestDataResource クラスの before メソッド内の dataSource を利用しているところで NullPointerException が出てテストが実行できませんでした。
※TestDataResource クラスの before, after メソッド内で実現しようとしていたのは、DbUnit を利用して既存データのバックアップ取得&テストデータへの入替、バックアップからのリストアの処理です。
public class TestDataResource extends ExternalResource {
@Autowired
private DataSource dataSource;
@Override
protected void before() throws Throwable {
IDatabaseConnection conn = new DatabaseConnection(dataSource.getConnection());
.....
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class SampleControllerTest {
@Rule
public TestDataResource testDataResource = new TestDataResource();
@Test
.....
}
この後調べて修正したのは以下の2点です。
- そもそもクラス内で @Autowired をフィールドに付加するのにクラスに何もアノテーションを付加していなかったので、@Component アノテーションを付加しました。
- ExternalResource を使う時のサンプルコードとして
public TestDataResource testDataResource = new TestDataResource();
の形式でテストクラスに定義するよう書かれている場合が多かったのですが、= new TestDataResource()
でインスタンスを生成するのではなく @Autowired アノテーションを付加して Spring のDIコンテナにインスタンスを生成してインジェクションしてもらうようにしました。
修正後のソースは以下のようになります。これで TestDataResource クラスの before, after メソッド内で dataSource を利用できるようになりました。
@Component
public class TestDataResource extends ExternalResource {
@Autowired
private DataSource dataSource;
@Override
protected void before() throws Throwable {
IDatabaseConnection conn = new DatabaseConnection(dataSource.getConnection());
.....
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class SampleControllerTest {
@Rule
@Autowired
public TestDataResource testDataResource;
@Test
.....
}