はじめに
この記事はSpring Data JPAが提供するWeb supportの「@PathVariable
のid検索して引数に設定してくれる」機能を使っている場合に、そのハンドラメソッドをどうやってテストすればいいかを説明したものです。記事中で説明しているコードは以下に置いてあります。
https://github.com/junjis0203/spring-jpa-web-unittest
Spring Data JPAのWeb support
Spring Data JPAにはWeb support機能があります。これを使うと、
@GetMapping("/users/{id}")
public User show(@PathVariable("id") User user) {
return user;
}
このように自分でidからUserエンティティを検索しなくていいのでハンドラメソッドを非常にシンプルにできます。なお裏側はUserクラスに対応するRepository使って検索しているのでRepositoryインターフェース自体は必要です。
よしではテストするぞ
・・・どうやって(。´・ω・)?
検索されるエンティティはどう準備すればいいの?Spring Data JPAみたいに@DataJpaTest
付けてTestEntityManager使う?idは?自分で指定しておけばいい?
まあ出てきたエラー見てから考えるか、てことでテストを作成、
@Test
public void userExists() throws Exception {
mockMvc.perform(get("/users/{id}", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("name", is("alice")));
}
実行。なおgithubに置いてあるのは最終形なので同じようなエラーが出ることを確認したい人はConfigクラスに付いている@TestConfiguration
外してください。
java.lang.AssertionError: Status expected:<200> but was:<500>
コンソールの方にはこんなのが出ていた(長いので適当に改行)。やはりSpring Data JPAのWeb supportは動かないようだ。
2019-08-23 22:55:51.036 WARN 8140 --- [ main]
.w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException:
Failed to convert value of type 'java.lang.String' to required type 'com.example.demo.User';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type 'java.lang.String' to required type 'com.example.demo.User':
no matching editors or conversion strategy found]
本題:テストをするためのセットアップ
テスト環境でWeb supportが動かないからと言ってControllerにRepositoryをDIさせたくないし何か方法はないかと「spring jpa pathvariable test」で検索したところ以下のページが引っかかりました。
https://stackoverflow.com/questions/40240547/combine-webmcvtest-with-pathvariable-annotated-controllers-in-spring-boot-1-41
なるほどWebMvcConfigurer.addFormattersで登録すればいいのか。
というわけでこれを参考にテストを書いていたのですが、複数のエンティティが必要な状況に遭遇したのでもう少しかっこいいConverterにしました。
private static Map<String, User> userMap;
@Before
public void setUp() {
userMap = new HashMap<>();
userMap.put("1", User.builder().name("alice").mail("alice@example.com").build());
}
@TestConfiguration
static class Config implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// emulate Spring Data JPA Web support
registry.addConverter(String.class, User.class, id -> userMap.get(id));
}
}
やっていることは
- StringとUserの対応Mapを
@Before
付けたメソッドで用意。@BeforeClass
でもいい気はするけどテストごとにリセットされる方がいい気もする - ConverterはMap使って返すだけ(Stack Overflowでは存在しないidに対してIllegalArgumentException投げてるけど「本物」の挙動的にはnull返す方が正しい)
- static classが参照できるようにMapもstaticにしないといけないのがダサいが仕方ない
ともかくこれでテストができました。
おわりに
今回はSpring Data JPAが提供するWeb supportを使ってる場合にどうやってテストすればいいのかを説明しました。
公式にテストする方法用意しておいてくれよと思うのですが、だったら自分で作ってプルリク送るのがいいのですかね。Springの動作理解して適切な「設定の書き方」構築するまで相当かかりそうですが・・・