概要
下記のような構成にて JUnit にてテストを実施した際に、表題の現象が発生いたしました。
src
|- main
| |- resources
| | |- db.migration/V1__createSchema.sql (flyway のマイグレーションDDL)
| |- application-development.properties
|- test
| |- resources
| |- application-unit.properties
flyway.schemas=PUBLIC
flyway.locations=filesystem:src/main/resources/db/migration/
// DI コンテナにて管理していないクラスからも application.properties にて定義された値を取得できるようにする
public final class EnvironmentHelper {
private static Environment environment;
static {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
environment = context.getEnvironment();
}
}
補足
下記の構成のように test 配下に flyway のリソースを配置すると早かった、、、気がします。。。
src
|- main
| |- resources
| |- application-development.properties
|- test
| |- resources
| | |- db.migration/V1__createSchema.sql
| |- application-unit.properties
また、main, test 配下に flyway の DDL を配置すると、flyway のファイルが 2 つ存在するために起動時エラーが発生する動作となりました。
原因についての推測
下記のようなログが複数回出力されていることから、ApplicationContext を初期化する際、ディレクトリ内の class ファイルをサーチしているものと考えられます。
DEBUG-2017-09-15 15:45:03,474---org.springframework.core.io.support.PathMatchingResourcePatternResolver-775-main-Searching directory [/Users/hoge/root/out/test/classes/example] for files matching pattern [/Users/hoge/root/out/test/classes/example/**/*.class]
DEBUG-2017-09-15 15:45:03,505---org.springframework.core.io.support.PathMatchingResourcePatternResolver-775-main-Searching directory [/Users/hoge/root/out/test/classes/example/helper] for files matching pattern [/Users/hoge/root/out/test/classes/example/**/*.class]
DEBUG-2017-09-15 15:45:03,537---org.springframework.core.io.support.PathMatchingResourcePatternResolver-775-main-Searching directory [/Users/hoge/root/out/test/classes/example/service] for files matching pattern [/Users/hoge/root/out/test/classes/example/**/*.class]
… many similar logs ….
上記ログが複数回出力されます。
回避策
こちら の記事を拝見させていただき、ApplicationListener を使用して、Environment オブジェクトを取得して、静的にアクセスできるようにしました。
また、アプリケーションの起動にてハンドリングされるイベントを調査したところ、下記の順にてログが出力されたことから、ApplicationReadyEvent クラスにて上記処理を実施いたしました。
INFO -example.spring.ApplicationEventHandler-17-main-org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent
INFO -example.spring.ApplicationEventHandler-17-main-org.springframework.context.event.ContextRefreshedEvent
INFO -example.spring.ApplicationEventHandler-17-main-org.springframework.boot.context.event.ApplicationReadyEvent
INFO -example.spring.ApplicationEventHandler-17-main-org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent
INFO -example.spring.ApplicationEventHandler-17-main-org.springframework.context.event.ContextRefreshedEvent
@Component
@Slf4j
public class ApplicationEventHandler implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("application is ready to start...");
Environment environment = event.getApplicationContext().getEnvironment();
EnvironmentHelper.setEnvironment(environment);
}
}
参考情報
タイトル : Interface ApplicationListener
アドレス : https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationListener.html
タイトル : Class ApplicationReadyEvent
アドレス : https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/event/ApplicationReadyEvent.html
補足
上記の方針を採用したおかげで、環境ごとに用意していた @Profile を定義したクラスは不要かと思いましたが、テスト用の Bean を Injectionするために以前として下記のようなクラスが必要でした。
@TestConfiguration
@Profile("test")
@PropertySource("classpath:application-test.properties")
public class TestEnvironment {
}
おそらく下記のように、テストクラスの基底クラスにて Bean を取得しているためであると思います。
@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public abstract class AbstractBaseTest {
@Rule
//public にしないと怒られる
public TestResource testResource;
public void setUp() {
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
testResource = context.getBean(TestResource.class);
}
}
@Autowired でなぜかテストクラスにテスト用のモジュールをバインドできなかったために下記のように実装しております。。TestResource を Bean に登録するクラス内にて Environment を Autowired しておりますが、@Profile クラスが存在しない場合、アプリケーション開始時に期待するキー項目を Environment から取得できませんでした。