6
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MockMVC で Spring Security に対応させる

Last updated at Posted at 2018-11-17

はじめに

以前、MockMvcを使う記事を書きました。その後、Spring Securityを使ったアプリで MockMvcを上手く動作させられませんでした。SpringやJUnitのバージョンの違いか、色々な記事があるのですが、どれの方法を試しても駄目でした。それらの記事を参考にして試しながら、何とかテストコードが動かせるようになったので、記事にまとめます。

以前の記事でまとめたポイント

  • JUnit5を使う為にアノテーションを差し替え
  • テスト用コンフィグクラウを準備

以前の記事: SpringBootとJUnit5でMockMvcを使うには

// テストクラス
@ExctendWith(SpringExtension.class)
@WebMvcTest(XxxController.class)

// テスト用コンフィグクラス
@Configuration
@ComponentScan({"パッケージ名"})

今回の遭遇した問題

①アノテーション

例外が発生するので、どのアノテーションを使えばいいかを試行錯誤しました。
※記事末尾のサンプルをご参照下さい。

②HTTP403エラー Forbidden

Spring Securityを使うと、CSRF対策がデフォルトで有効になる為、HTTP403が発生。
MockMVCでリクエストする際、SecurityMockMvcRequestPostProcessors.csrf() を追加すればエラーを回避できる。

mockMvc.perform(post("http://localhost:8080/customers")
	.contentType(MediaType.TEXT_HTML)
	.with(SecurityMockMvcRequestPostProcessors.csrf())
...

③HTTP404エラー

リクエストURLをRequestMappingの記述部分しか書いていなかった為に発生。
※色々試していたアノテーションが原因かと勘違いして意外と時間がかかった。

java.lang.AssertionError: Range for response status value 404 expected:<SUCCESSFUL> but was:<CLIENT_ERROR>

④Thymeleafで出力しているログインユーザー情報の参照

これが一番難しかったです。
これは正にと思える @WithMockUser と言うアノテーションがあったので、設定するも、Thymeleafでログインユーザー情報が参照できず。

// エラーメッセージ
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'principal.user' of bean class 
// @WithMockUser で設定したユーザーを指定して見たが駄目だった例
mockMvc.perform(get("http://localhost:8080/customers")
	.contentType(MediaType.TEXT_HTML)
	.with(SecurityMockMvcRequestPostProcessors.csrf())
	.with(user("user1"))

org.springframework.security.core.userdetails.UserDetailsService インターフェースを実装したクラスを利用する事で参照できるように。

// まず、テストクラスのメンバ変数にサービスクラスを作成
@Autowired
private LoginUserDetailsService loginUserDetailsService;

// 次に、テストクラスのメソッドで、以下を実装
// インプリメントしたloadUserByUsername()メソッドでユーザー情報を取得
LoginUserDetails userDetails = (LoginUserDetails) loginUserDetailsService.loadUserByUsername("admin1");

// 取得したユーザー情報を渡す
mockMvc.perform(get("http://localhost:8080/customers")
	.contentType(MediaType.TEXT_HTML)
	.with(SecurityMockMvcRequestPostProcessors.csrf())
	.with(user(userDetails))

⑤フォーム情報

MockHttpServletRequestBuilder.param()メソッドを利用するだけ。

コード

テストクラスに設定するアノテーションが変わりました。
前回必須だと思ったコンフィグクラスは不要でした。

@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestInstance(Lifecycle.PER_CLASS)
@AutoConfigureMockMvc
public class CustomerControllerTest {

	@Test
	public void test() throws Exception {

		LoginUserDetails userDetails = (LoginUserDetails) loginUserDetailsService.loadUserByUsername("admin1");

		//@formatter:off
		mockMvc.perform(post("http://localhost:8080/customers/create")
			.contentType(MediaType.TEXT_HTML)
			.with(SecurityMockMvcRequestPostProcessors.csrf())
			.with(user(userDetails))
			.param("firstName", "太郎")
			.param("lastName", "山田")
		)
		.andExpect(SecurityMockMvcResultMatchers.authenticated())
		.andExpect(MockMvcResultMatchers.status().is3xxRedirection())
		.andExpect(MockMvcResultMatchers.redirectedUrl("/customers"))
		.andReturn();
		//@formatter:on
	}
}

参考資料

6
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?