【問題解決】MyBatisのTestで「Failed to replace DataSource with an embedded database for tests. 」というエラー発生


# 環境
* mybatis-spring-boot-starter: 1.3.1
    - mybatis: 3.4.5
* mybatis-spring-boot-starter-test: 1.3.1
* postgresql jdbc : 42.1.4
* Spring Boot 1.5.9
* Java8
* PostgreSQL 9.6

# やりたいこと
MyBatisを使ってDBにアクセスしています。
今、`HotelDao.java`の`findById`メソッドのテストを実施したいです。

```java:HotelDao.java
@Component
public class HotelDao {

	private final SqlSession sqlSession;

	public HotelDao(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	public Hotel findById(long id) {
		return this.sqlSession.selectOne("findHotelById", id);
	}

}
```

```xml:HotelMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo2.mybatis.mapper.HotelMapper">

    <select id="findHotelById" resultType="com.example.demo2.mybatis.domain.Hotel">
        select * from hotel where city = #{id}
    </select>
</mapper>
```

# 問題

以下のような、`findById`メソッドを確認するテストコードを作成しました。

```java:HotelDao.java
@RunWith(SpringRunner.class)
@MybatisTest
@Import({HotelDao.class})
public class HotelDaoTest {

	@Autowired
	private HotelDao hotelDao;

	@Test
	public void test() {
		Hotel hotel = hotelDao.findById(1);
		System.out.println(hotel);
        //assert
	}

}
```

JUnitで実行したら、以下のエラーが発生しました。

```txt:console
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2018-01-24 00:24:03.507 ERROR 46308 --- [           main] o.s.boot.SpringApplication               : Application startup failed

org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'com.example.demo2.mybatis.dao.HotelDao': 
Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'sqlSessionTemplate' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: 
Unsatisfied dependency expressed through method 'sqlSessionTemplate' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: 
Unsatisfied dependency expressed through method 'sqlSessionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is java.lang.IllegalStateException: 
Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoconfigureTestDatabase.
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
...
```

※読みやすくするため、適当な箇所に改行を入れました。

# 原因
エラーの最後の方に、「Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoconfigureTestDatabase.」と記述されています。

書いてある内容は、

* DataSourceをテスト用の組み込みデータベースに置き換えられなかった。
* 組み込みデータベースを使いたいなら、サポートされている組み込みデータベースをクラスパスに追加するか、`@AutoconfigureTestDatabase`の`replace`属性を調整。

です。

`@MyBatisTest`を付与すると、デフォルトでは組み込みデータベースを設定します。しかし今のプロジェクトでは、組み込みデータベース用の設定(pom.xmlに定義していない）を行っていないので、エラーが発生しているようです。

>The @MybatisTest can be used if you want to test MyBatis components(Mapper interface and SqlSession). By default it will configure MyBatis(MyBatis-Spring) components(SqlSessionFactory and SqlSessionTemplate), configure MyBatis mapper interfaces and configure an in-memory embedded database. 

※ http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/ 引用




# 解決1（`@AutoConfigureTestDatabase`を設定）
テスト時にPostgreSQLを使う場合の対応方法です。

`@AutoConfigureTestDatabase(replace = Replace.NONE)`を追加しました。

```java:HotelDaoTest.java
@RunWith(SpringRunner.class)
@MybatisTest
@AutoConfigureTestDatabase(replace = Replace.NONE) //追加
@Import({HotelDao.class})
public class HotelDaoTest {

	@Autowired
	private HotelDao hotelDao;

	@Test
	public void test() {
		Hotel hotel = hotelDao.findById(1);
		System.out.println(hotel);
	}

}
```

以下のサイトを参考にしました。

* [Using a real database - MyBatis](http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/#Using_a_real_database)
* [Spring-boot-starter-test cannot run database integration test
](https://stackoverflow.com/questions/40659912/spring-boot-starter-test-cannot-run-database-integration-test)
* [Sprint Boot 1.4 @DataJpaTest - Error creating bean with name 'dataSource'
](https://stackoverflow.com/questions/41315386/sprint-boot-1-4-datajpatest-error-creating-bean-with-name-datasource/41316559)






## `@AutoconfigureTestDatabase`について
* [Annotation Type AutoConfigureTestDatabase](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.html)

### `@AutoconfigureTestDatabase`のパッケージ
Spring Boot 1.5.9では、`@AutoconfigureTestDatabase`が2種類あります。

* `org.springframework.boot.test.autoconfigure.orm.jp`
* `org.springframework.boot.test.autoconfigure.jdbc`

`*.orm.jp`の方は非推奨なので、`*.jdbc`の方を使います。



# 解決2（テスト時は組み込みデータベースを利用）
HSQLDBを利用します。

```xml:pom.xml
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>runtime</scope>
</dependency>
```

テストデータを作成するSQLを`src/test/resource/`直下に置きます。
そうすると、テスト実行前にこのSQLが実行されます。

```sql:src/test/resource/schema.sql
CREATE TABLE hotel
(
  city integer NOT NULL ,
  name character varying(256),
  address character varying(256),
  zip character varying(256),
  CONSTRAINT pkey PRIMARY KEY (city)
);

insert into hotel(city, name, address, zip) values (1, 'Conrad Treasury Place', 'William & George Streets', '4001')
```


## 補足

## 組み込みデータベースについて
アプリケーションに組み込まれたデータベース。事前にデータベースを用意する必要がなく、アプリケーションの起動時にデータベースが構築されます。

* [埋込RDBの利用（Derby,HSQLDB,H2ライブラリの性能比較）](http://krr.blog.shinobi.jp/java/%E5%9F%8B%E8%BE%BCrdb%E3%81%AE%E5%88%A9%E7%94%A8%EF%BC%88derby-h2-hsqldb%E3%83%A9%E3%82%A4)

### Springでのデータベースの初期化
`schema.sql`だけでなく、`data.sql`というファイル名でも、自動的に実行されます。

>Spring Boot can automatically create the schema (DDL scripts) of your DataSource and initialize it (DML scripts): it loads SQL from the standard root classpath locations schema.sql and data.sql, respectively.

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html#howto-initialize-a-database-using-spring-jdbc 引用


# 参考にしたサイト
* [Spring-boot-starter-test cannot run database integration test
](https://stackoverflow.com/questions/40659912/spring-boot-starter-test-cannot-run-database-integration-test)
* [Sprint Boot 1.4 @DataJpaTest - Error creating bean with name 'dataSource'
](https://stackoverflow.com/questions/41315386/sprint-boot-1-4-datajpatest-error-creating-bean-with-name-datasource/41316559)
* [@JdbcTest detect class annotated @SpringBootApplication](https://stackoverflow.com/questions/42722480/jdbctest-detect-class-annotated-springbootapplication)
* [mybatis-spring-boot-test-autoconfigure](http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/)