Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

この記事の概要

  • 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
koyo-miyamura
九州大学大学院システム情報科学府卒 面白法人カヤック・サーバサイドエンジニア note: https://note.mu/m_k9071 ポートフォリオ: https://koyo-miyamura.github.io/portfolio/
http://maple-daily.com/
fukuokaex
エンジニア/企業向けにElixirプロダクト開発・SI案件開発を支援する福岡のコミュニティ
https://fukuokaex.fun/
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした