LoginSignup
3
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-10-31

この記事の概要

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

※Phoenix v1.3

問題

Phoenixの勉強がてらシンプルなブログアプリを作ってる際にテストを書いたときに結構ハマったのでシェア

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

テストコード

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はテストヘルパーとして以下のように定義しています

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

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

  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

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

実装

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

Finished in 0.7 seconds
1 test, 0 failures

余談

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


  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
3
0
0

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
3
0