こんなことをするひとはいないと思いますが、せっかく苦しんだのでメモしておきます。

この文章の表記について

:grin: 1.4: 1.4の場合のやりかたです。
:smirk: 1.2: 1.2の場合のやりかたです。

実装関係

@EntityScan

:grin: 1.4: org.springframework.boot.autoconfigure.domain.EntityScan
:smirk: 1.2: org.springframework.boot.orm.jpa.EntityScan

DI

:grin: 1.4: 自明であれば@Autowiredを付けなくてもDIしてくます。
:smirk: 1.2: @Autowired付けましょう。

テスト関係

テストランナー

:grin: 1.4: @RunWith(SpringRunner.class)
:smirk: 1.2: @RunWith(SpringJUnit4ClassRunner.class)

Springを使ったテスト

:grin: 1.4: @SpringBootTestを付ける
:smirk: 1.2: @SpringApplicationConfiguration(classes = アプリケーションクラス)を付ける

http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/SpringApplicationConfiguration.html

データベース統合テスト

:grin: 1.4: @DataJpaTestをテストクラスに付ける。
:smirk: 1.2: ちと大変。

@DataJpaTestはこんなことをしてくれているそうです。

  1. インメモリデータベースを設定
  2. Hibernate/Spring Data/Datasourceを設定
  3. @EntityScanを実行
  4. SQLログを有効化

全部がんばらないといけないかと思いましたが、これだけのことをやれば大丈夫そうです。

  1. @EntityScan@EnableJpaRepositoriesを付けたアプリケーションコンテキストクラスを作成。
  2. TestEntityManagerクラスを作成(1.4からぱくる)
  3. TestEntityManagerを返すBeanを作成(1.4からぱくる)
  4. テストクラスに@Transactional@SpringApplicationConfigurationを付ける。

順番にやっていきます。

@EntityScan@EnableJpaRepositoriesを付けたアプリケーションコンテキストクラスを作成

TestApplication.java

@SpringBootApplication
@EnableJpaRepositories(レポジトリクラスの場所)
@EntityScan(エンティティクラスの場所)
@EnableJpaAuditing // これは今回関係ない
public class TestApplication {

}

TestEntityManagerクラスを作成(1.4からぱくる)

元ソースはここにあります。
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/

TestEntityManager.java

public class TestEntityManager {

    private final EntityManagerFactory entityManagerFactory;

    public TestEntityManager(EntityManagerFactory entityManagerFactory) {
        Assert.notNull(entityManagerFactory, "EntityManagerFactory must not be null");
        this.entityManagerFactory = entityManagerFactory;
    }

    public <E> E persist(E entity) {
        getEntityManager().persist(entity);
        return entity;
    }

    public void flush() {
        getEntityManager().flush();
    }

    public <E> E persistAndFlush(E entity) {
        persist(entity);
        flush();
        return entity;
    }

    public void detach(Object entity) {
        getEntityManager().detach(entity);
    }

    public <E> E find(Class<E> entityClass, Object primaryKey) {
        return getEntityManager().find(entityClass, primaryKey);
    }

    public final EntityManager getEntityManager() {
        EntityManager manager = EntityManagerFactoryUtils
                .getTransactionalEntityManager(this.entityManagerFactory);
        Assert.state(manager != null, "No transactional EntityManager found");
        return manager;
    }
}

TestEntityManagerを返すBeanを作成(1.4からぱくる)

TestEntityManagerConfigure.java

@Configuration
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class TestEntityManagerConfigure {

    @Bean
    @ConditionalOnMissingBean
    public TestEntityManager testEntityManager(
            EntityManagerFactory entityManagerFactory) {
        return new TestEntityManager(entityManagerFactory);
    }

}

テストクラスに@Transactional@SpringApplicationConfigurationを付ける

MySomeRepositoryTest.java

import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestApplication.class)
@Transactional
public class MySomeRepositoryTest {

    @Autowired
    private MySomeRepository instance;

    @Autowired
    private TestEntityManager em;

    @Test
    public void testSomeMethod {
     // ...
    }

}


モック化したインスタンスをインジェクトする

:grin: 1.4: モック化したいインスタンス変数に@MockBeanを付ける
:smirk: 1.2: モック化したインスタンスを返す@Beanを定義して@Primaryを付ける

1.4の場合

MySomeTest.java

    @MockBean
    private MyTargetClass mock;

1.2の場合

特定の@Configurationクラスだけ有効になるように、@Profileを付けています。

MySomeTest.java

@ActiveProfiles("MySomeTest")
public class MySomeTest {
    @Autowired
    private MyTargetClass mock;
}

MySomeTestConfig.java

@Configuration
@Profile("MySomeTest")
public class MySomeTestConfig {
    @Bean
    @Primary
    public MyTargetClass myTargetClass() {
        return Mockito.mock(MyTargetClass.class);
    }
}

旅は続く。。。

また何か発見があったら追記していきます。