SpringBootでDBも絡めた単体テストを書いてみる
概要
Spring絡みのことを色々書いたので、SpringBootでの単体テストについてもちょっとだけ書いとこう
SpringBootでの単体テスト
メソッド単位でちゃんと書く的なことじゃなくて大元のメソッドからDBまでぐるっと動作させて期待値通りのものが出てるか確認してみます。
書いてみたテスト
とりあえず書いた最小限のテスト構成を書いておこう。
テストクラス
@RunWith(SpringRunner.class)
- これ書くと@AutowiredとかSpringの機能が使えるようになるらしい
@SpringBootTest
- application.propertiesなどを読み込めるようにするらしい
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class })
- Listenerを設定この辺設定しとくと色々できるようになるらしい
@DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class)
- データベースをCSVで読み込めるように設定
@DatabaseSetup(value = "/xyz/ucwork/future/mutation/")
- テスト実行前のデータベースの状態を定義できる
@ExpectedDatabase(value = "/xyz/ucwork/future/mutation/expect/", table = "members", assertionMode = DatabaseAssertionMode.NON_STRICT)
- テスト実行後のデータベースの期待値を定義できる
/future/src/test/java/xyz/ucwork/future/resolvers/MutationTest.java
package xyz.ucwork.future.resolvers; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import com.github.springtestdbunit.assertion.DatabaseAssertionMode; import xyz.ucwork.future.CsvDataSetLoader; @RunWith(SpringRunner.class) @SpringBootTest @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class) public class MutationTest { @Autowired private Mutation mutation; @Test @DatabaseSetup(value = "/xyz/ucwork/future/mutation/") @ExpectedDatabase(value = "/xyz/ucwork/future/mutation/expect/", table = "members", assertionMode = DatabaseAssertionMode.NON_STRICT) public void testRegistMember() { mutation.registMember("testName"); } }
データベースの状態をCSVで設定できるようにするためのクラス
お作法のようにそのまんま書いて、テストクラスの@DbUnitConfigurationアノテーションで指定
/future/src/test/java/xyz/ucwork/future/CsvDataSetLoader.java
package xyz.ucwork.future; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.csv.CsvURLDataSet; import org.springframework.core.io.Resource; import com.github.springtestdbunit.dataset.AbstractDataSetLoader; public class CsvDataSetLoader extends AbstractDataSetLoader { @Override protected IDataSet createDataSet(Resource resource) throws Exception { return new CsvURLDataSet(resource.getURL()); } }
テスト実行前のデータベース状態設定
カラム名と設定したい値を書く。
/future/src/test/resources/xyz/ucwork/future/mutation/members.csv
"id","name","created_at","updated_at"
table-ordering.txtにテーブル名を改行入れながら書くと上から順番にテーブルに書き込んでくれる
/future/src/test/resources/xyz/ucwork/future/mutation/table-ordering.txt
members
テスト実行後のデータベース状態設定
期待値確認したいカラムと値だけ書いとく。auto_incrementのidや都度変わる日時は期待値に含めてない
/future/src/test/resources/xyz/ucwork/future/mutation/expect/members.csv
"name" "testName"
/future/src/test/resources/xyz/ucwork/future/mutation/expect/table-ordering.txt
members
プロパティーを設定する
単体テストを実施するデータベースを作成して設定する
/future/src/test/resources/application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/future_unit_test username: root password: root driver-class-name: com.mysql.jdbc.Driver
余談
ちょっくらgraphql-spring-boot-starter
のバージョンを5.0.2に上げてみたらこんなエラーが出てきた。
4.4.0に戻したら治った。優秀なエンジニアの人たちがモミにもんでよろしく動くようにしてくれてると祈って見なかったことにしおく
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverEndpointExporter' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available
まとめ
本当はメール送信をスタブ化したときにすごく苦労したのでそのへん書こうかとおもいましたが、もう気力がなくなってきたのでGithubのテストの場所を貼り付けておきます。
「AmazonSESのモック化」と「Mockito2.x系でWhiteBoxがDepricatedされた問題」が大きくつまずいたところな気がする