Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@koyo-miyamura

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

※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
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?