Spring 3.1 新機能 - AnnotationConfigContextLoader
3.0 から Javaconfig という XML でなく Java のコードで Spring の設定が記述できる機能が追加されていましたが、この機能が Test でも使えるようになりました。
従来 3.0 のテストケースは @ContextConfiguration を使用してこんな定義をしていました。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/META-INF/spring/*.xml") public class AccountRepositoryTest {
3.1 からは AnnotationConfigContextLoader を指定すると Javaconfig をロード可能なります。面白いのは内部クラスで @Configuration が付いているものを自動でロードしてくれることです。
こんな使い方が出来ます。
package org.yamkazu.spring31; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import javax.inject.Inject; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.transaction.annotation.EnableTransactionManagement; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class) public class AccountRepositoryWithJavaConfigTest { @Configuration @EnableTransactionManagement @ComponentScan(basePackages = "org.yamkazu.spring31", excludeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = TestConfig.class) }) static class TestConfig { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource()); return emf; } @Bean public DataSource dataSource() { // @formatter:off return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2) .addScript("classpath:/schema.sql") .addScript("classpath:/test-data.sql") .build(); // @formatter:on } @Bean public JpaTransactionManager transactionManager() { return new JpaTransactionManager(); } } @Inject AccountRepository repository; @Test public void test() throws Exception { Account account = new Account(); account.setName("aaa"); repository.persist(account); Account find = repository.find(account.getId()); assertThat(find.getName(), is(equalTo("aaa"))); } }
テスト対象の周辺クラスを、モックに差し替えるといったテストのコンテキストで色々小回りが効きそうな印象です。
ちょっとわからなかったのが、@ComponentScanを使用する場合、かつ自身のテストクラスのパッケージが含まれている場合(テストクラスはテスト対象と同じパッケージにするのでかぶること多いじゃないかなぁ)、@ComponentScan と AnnotationConfigContextLoader が 同じBeanを登録しようとしてコンフリクトしてしまうことです。excludeFiltersを設定することで回避できましたが、これが解なのかは不明。
任意の@Configurationのクラスしてする場合は@ContextConfigurationのclassesに指定すれば良いです。