きり丸アドベントカレンダー2020の16記事目です。
自システムのデータをREST APIで公開すると、非同期で処理ができるようになるのでフロントのシステムをSPAにできたりします。第三者にデータを提供することもできます。
ぜひ、REST APIでシステムを公開することを考えてみましょう。
ちなみに、REST API自体は前に登壇した時の資料があるので、見ていただけるとうれしいです。
ゴール
環境
- Java
- 15
- org.springframework.boot:spring-boot-starter-Web
- 2.4.0
- org.springframework.boot:spring-boot-starter-test
- 2.4.0
- org.springframework.boot:spring-boot-starter-security
- 2.4.0
- org.springframework.security:spring-security-test
- 5.4.1
- (lombok)
手順
テスト用の依存関係を追加する
基本的にコントローラ層のテストは@WebMvcTest
でやるのがベストです。なおSpring SecurityでBASIC認証をかけていると、@SpringBootTest
の使用時は正常終了しますが、@WebMvcTest
の使用時はHttpStatus401:Unauthorized(未認証)が発生してしまいます。
それが発生しないようにSpring Securityのテスト用のライブラリとして、org.springframework.security:spring-security-test
を依存関係に含めます。
ファイル名:build.gradle
dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compile 'org.springframework.boot:spring-boot-starter-security' compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' testCompile 'org.springframework.security:spring-security-test:5.4.1' // 重要 }
RestAPIのコードを書く
基本的には@Controller
と同じように、@RestController
を使用すればいいです。
次のコードでは「/v1/todos/{userId}」にGETメソッドでアクセスしたときに呼ばれます。{userId}は自由に変更できる項目です。「/v1/todos/admin」とした場合はパラメータに「admin」が渡されます。パラメータは@PathVariable
で受け取れるようになります。
データがある場合は、HttpStatus:200にしたうえで、ResponseBodyに返却結果を返します。ない場合は、HttpStatus:404を返却します。
ファイル名:TodoRestController.java
@RequiredArgsConstructor @RestController @RequestMapping("/v1/todos") public class TodoRestController { private final TodoRepository todoRepository; @GetMapping("/{userId}") public ResponseEntity<List<TodoDto>> get(@PathVariable String userId) { final List<TodoDto> list = todoRepository.findList(userId); if (list.isEmpty()) { return ResponseEntity.notFound().build(); } else { return ResponseEntity.ok(list); } } }
データがあるときの返却結果例:
[{"id":1,"userId":"admin","action":"2"},{"id":2,"userId":"admin","action":"3"},{"id":3,"userId":"admin","action":"4"}]
テストコードを書く
@WebMvcTest
にテスト対象のコントローラクラスを渡します。MockMvc
を使用するので、@Autowired
を付与します。@MockBean
はテスト対象のクラスでDIしている項目に対して使用します。
Spring SecurityでBASIC認証を行っている場合、@WithMockUser
を使用して認証している状態にする必要があります。
mockMvc
でアクセスした結果を元に、HttpStatusやBodyを確認します。
ファイル名:TodoRestControllerTest.java
@WebMvcTest(TodoRestController.class) @WithMockUser(value = "spring") public class TodoRestControllerTests { @Autowired private MockMvc mockMvc; private final String rootUrl = "/v1/todos"; @MockBean private TodoRepository todoRepository; @Test void success() throws Exception { // GIVEN when(todoRepository.findList("spring")) .thenReturn(List.of( TodoDto.builder().id(1).userId("spring").action("醤油").build(), TodoDto.builder().id(2).userId("spring").action("豆腐").build() )); // Language=json // TODO: テキストブロック機能はJava13の機能です // 文字エスケープしなくていいので見やすいです String expected = """ [{ "id": 1, "userId": "spring", "action": "醤油" }, { "id": 2, "userId": "spring", "action": "豆腐" } ]"""; this.mockMvc.perform(get(rootUrl + "/spring")) .andExpect(status().isOk()) .andExpect(content().json(expected)); } @Test void _404_not_found() throws Exception { when(todoRepository.findList("spring")) .thenReturn(List.of()); this.mockMvc.perform(get(rootUrl + "/spring")) .andExpect(status().isNotFound()) ; } }
実際にアクセスするとき
BASIC認証を行う必要があります。
cURLでは次のどちらかでBASIC認証をしつつ、アクセスできます。 ※ あくまで私が用意したID:PWですので、BASIC認証する際は自分で用意したID:PWにしてください。
curl http://localhost:8080/v1/todos/admin -u admin:pass curl http://admin:pass@localhost:8080/v1/todos/admin
ソースコード
アドベントカレンダー16日目。
github.com
終わりに
普段使用しているシステムはRestControllerを使用しているので、検証が非常に簡単に終わりました。しかし、Spring Securityと組み合わせたことはなかったので、@WithMockUser
にたどり着くまでに時間かかってしまいました。昔であれば、secure=false
等々を設定するだけでうまくいったようなのですが、新しくなってから設定方法が分からなかったです。
ある程度歴史があると、古い情報にばっかりアクセスしてしまうのでわかりづらいですね…。
Spring Securityが絡むと、いろいろと難易度が高くなるので情報を調べづらいです。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。
参考記事
敬称略。
baeldung:Test a REST API with Java www.baeldung.com
公式リファレンス:Spring Security Reference docs.spring.io
類似記事
きり丸アドベントカレンダー2020 adventar.org
きり丸のHerokuページ
https://kirimaru-todoapp.herokuapp.com/
17日目のアドベントカレンダーの記事 https://nainaistar.hatenablog.com/entry/2020/12/17/083000nainaistar.hatenablog.com