Spring フラッシュスコープを利用したリダイレクトと単体テスト

あるエンドポイントから別のエンドポイントにリダイレクトさせる際に、フラッシュスコープを利用するとリダイレクト先へパラメータを引き継がせることができる。
# コントローラ
以下のコントローラで`/from`にGETでアクセスした際に、`/to`へリダイレクトさせる。
リダイレクト元の処理で、`bookName`と`buyerList`をフラッシュスコープに突っ込んで、リダイレクト先へ引き継がせる。

```java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.Arrays;
import java.util.List;

@Controller
public class DemoController {

    @GetMapping(path = "/from")
    public String from(RedirectAttributes redirectAttributes) {

        redirectAttributes.addFlashAttribute("bookName", "ドラえもん");

        List<String> buyerList = Arrays.asList("野比", "剛田");
        redirectAttributes.addFlashAttribute("buyerList", buyerList);

        return "redirect:/to";
    }

    @GetMapping(path = "/to")
    public String to(Model model) {
        System.out.println(model);
        return "demo";
    }
}
```
`/from`にアクセスすると`/to`にリダイレクトされ、引き継がれたパラメータがコンソールに表示される。

```
{bookName=ドラえもん, buyerList=[野比, 剛田]}
```
### ポイント
- リダイレクト元のメソッドで`RedirectAttributes`を引数に設定し、`.addFlashAttribute`で引き継ぐattribute名と内容を指定する。なお、attribute名を省略することもできるが、その場合はクラス名に応じてattribute名が自動設定される。
- リダイレクト先では引数に設定した`Model`の中にattributeが含まれている。内容を参照したい場合は`.getAttribute`でattribute名を指定する。

# 単体テスト
`/from`にアクセスした際に、パラメータを引き継いで`/to`へリダイレクトされることを確認する。

```java
package com.example.demo.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import java.util.Arrays;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DemoControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void redirectWithAttributes() throws Exception {
        mockMvc.perform(get("/from"))
                .andExpect(status().isFound())
                .andExpect(redirectedUrl("/to"))
                .andExpect(flash().attribute("bookName", "ドラえもん"))
                .andExpect(flash().attribute("buyerList", Arrays.asList("野比", "剛田")));
    }
}
```
### ポイント
- `andExpect()`で期待値通りに動いていることを確認。
- `status().isFound()`でHTTPステータスが`302`であることを確認。
- `redirectedUrl()`でリダイレクト先のパスをアサーション。
- `flash().attribute()`で引き継ぐパラメータのアサーション。

### `FlashMap`による取り出し
以下のように引き継ぎパラメータを`FlashMap`で取り出すことができる。余計なパラメータが入っていないことの確認や、単純な文字列表現ではアサーションできない場合はこちらを利用する。

```java

    @Test
    public void redirectWithAttributes() throws Exception {

        MvcResult mvcResult = mockMvc.perform(get("/from"))
                .andExpect(status().isFound())
                .andExpect(redirectedUrl("/to"))
                .andReturn();

        FlashMap flashMap = mvcResult.getFlashMap();

        assertThat(flashMap.size()).isEqualTo(2);
        assertThat(flashMap.get("bookName")).isEqualTo("ドラえもん");
        assertThat(flashMap.get("buyerList")).isEqualTo(Arrays.asList("野比", "剛田"));
```