Phoenixのテストで (Plug.Conn.AlreadySentError) the response was already sent が出るときはrecycleを使う

## この記事の概要
* Phoenixのテストで `(Plug.Conn.AlreadySentError) the response was already sent` が出るときは`recycle/1`を試してみる
* おそらく1回のテストブロックの中で2回以上getすると発生する？（違うかも

※Phoenix v1.3
## 問題
Phoenixの勉強がてら[シンプルなブログアプリ](https://github.com/koyo-miyamura/ex_blog_practice)を作ってる際にテストを書いたときに結構ハマったのでシェア

※Guardianというログイン処理周りのライブラリを使っています

### テストコード
```elixir:ex_blog\test\ex_blog_web\controllers\page_controller_test.exs
defmodule ExBlogWeb.PageControllerTest do
  use ExBlogWeb.ConnCase
  import ExBlog.Support.Utils
  alias ExBlog.Accounts

  @valid_attrs %{email: "some@example.com", name: "somename", password: "somepassword"}

  setup do
    {:ok, user} = Accounts.create_user(@valid_attrs)
    {:ok, user: user}
  end

    test "Get /", %{conn: conn, user: user} do
      conn = get conn, "/"
      assert html_response(conn, 200)
      conn =
        conn
        |> login_user(user)
        |> get("/")
      # Show <a href="/articles" when login successed
      assert html_response(conn, 200) =~ "<a href=\"#{article_path(conn, :index)}\">"
    end

end

```

ちなみにlogin_userはテストヘルパーとして以下のように定義しています

```elixir:ex_blog\test\support\utils.ex

defmodule ExBlog.Support.Utils do
  alias ExBlog.Accounts.User
  alias ExBlog.Accounts.Guardian

  def login_user(conn, %User{} = user) do
    Guardian.Plug.sign_in(conn, user)
  end

  def logout_user(conn, %User{} = user) do
    Guardian.Plug.sign_out(conn, user)
  end
end

```

これを実行すると以下のようなエラーが！

```bash
  1) test Get / (ExBlogWeb.PageControllerTest)
     test/ex_blog_web/controllers/page_controller_test.exs:13
     ** (Plug.Conn.AlreadySentError) the response was already sent
     code: |> login_user(user)
     stacktrace:
       (plug) lib/plug/conn.ex:1395: Plug.Conn.put_session/3
       (guardian) lib/guardian/plug.ex:190: Guardian.Plug.sign_in/5
       test/ex_blog_web/controllers/page_controller_test.exs:18: (test)
```

## 解決策
公式によるとrecycle/1 を使う必要があるみたいです
クッキー更新などの役割の様子
[Phoenix.ConnTest – Phoenix v1.3.4 | recycle/1](https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#recycle/1)

以下の例を見ると、postなどでは自動でrecycleしてるみたいなので、自前で使った関数でログインしたのが原因っぽいです
postを使ってログインすれば必要なさそう（未確認）
[Phoenix.ConnTest – Phoenix v1.3.4 | module-recycling](https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#module-recycling)

## 実装

```elixir:ex_blog\test\ex_blog_web\controllers\page_controller_test.exs
defmodule ExBlogWeb.PageControllerTest do
    ...

    test "Get /", %{conn: conn, user: user} do
      conn = get conn, "/"
      assert html_response(conn, 200)
      conn =
        conn
        |> recycle() # Important!!
        |> login_user(user)
        |> get("/")
      # Show <a href="/articles"> when login successed
      assert html_response(conn, 200) =~ "<a href=\"#{article_path(conn, :index)}\">"
    end

end

```

```bash
Finished in 0.7 seconds
1 test, 0 failures
```

## 余談
以下のようにテストを分けても動きました
クッキーを変化させるようなテスト書く場合は、一回のテストに2回以上getするのが良くないのかな？

```elixir

  describe "get /" do
    test "without login", %{conn: conn} do
      conn = get conn, "/"
      assert html_response(conn, 200) =~ "Simple blog by Phoenix"
    end
    test "logged in", %{conn: conn, user: user} do
      conn =
        conn
        |> login_user(user)
        |> get("/")
      # html_responce is \ escaped like \"hogehoge\", not "hogehoge"
      # so we need \ before "
      assert html_response(conn, 200) =~ "<a href=\"#{article_path(conn, :index)}\">"
    end
  end
```