なんか標準で対応してないんですよね。ログインならログインでそこ通してやればいいじゃんみたいな感じの開発者の1人の発言読んだ気がする。こっちはインテグレーションテストがしたいんじゃねーんだよユニットテストがしたいんだよ!と思いました。
これやるのすんごい苦労した。めちゃめちゃ苦労した。stackoverflow なんかにもあったけど、Phoenixの新しいバージョンじゃ通じないみたいでした。サッポロビームの方々、特に @darui_kara 氏にはたっぷりお世話になりました。ありがとうございました。
ではまず、セッションを使ったこんなアクションがあるとします:
def new(conn, _params) do
tmp_user = get_session(conn, :tmp_user)
changeset = User.changeset(%User{}, tmp_user)
render(conn, "new.html", changeset: changeset)
end
&get_session/2
を使っているのでどうにかセッションに値を埋め込みたいですね。
テスト側はmix phoenix.gen.html
なんかで生成した時はこんな感じだと思います:
test "renders form for new resources", %{conn: conn} do
conn = get conn, user_path(conn, :new)
assert html_response(conn, 200) =~ "New user"
end
このままテスト実行すると tmp_user が nil になるのでテスト失敗しますね。テスト側で&put_session/3
使いたいですね。この前のアクション実行してからとか、外部の通信とかあったらダルいですね。
そこで、test/support/controller_helper.ex
というファイルを作ります。あ、YourAppのところは適宜書き換えて下さい。
defmodule YourApp.ControllerHelper do
defmacro __using__(args) do
controller = Keyword.get(args, :controller)
quote bind_quoted: [controller: controller] do
use Plug.Test
@controller controller
def action(conn, action, params \\ %{}) do
conn = conn
|> put_private(:phoenix_controller, @controller)
|> Phoenix.Controller.put_view(Phoenix.Controller.__view__(@controller))
apply(@controller, action, [conn, params])
end
@session Plug.Session.init(
store: :cookie,
key: "_app",
encryption_salt: "yadayada",
signing_salt: "yadayada"
)
defp with_session_and_flash(conn) do
conn
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Plug.Conn.fetch_session()
|> Phoenix.ConnTest.fetch_flash()
end
end
end
end
雑に解説しますと、conn の初期化処理を自分でしてます。まず、標準で用意されている conn はセッションの準備をしていません。そこで&with_session_and_flash/1
というセッション部分の初期化だけを付け足した関数を用意しました。また、標準で用意されているget
,post
,put
,delete
などのコントローラーへのアクセス関数を通すと conn が再初期化されてしまうので初期化しないでアクセスする関数&action/3
を定義しています。
そして、テストファイルでuse
を入れてちょちょいと書き足します:
defmodule YourApp.UserControllerTest do
use YourApp.ConnCase
# この行追加
use YourApp.ControllerHelper, controller: YourApp.UserController
...
# 中身書き換え
test "renders form for new resources", %{conn: conn} do
conn = conn
|> with_session_and_flash
|> put_session(:tmp_user, @valid_attrs)
|> action :new, %{}
assert html_response(conn, 200) =~ "New user"
end
はい。put_session
,get_session
,put_flash
,get_flash
する前に、with_session_and_flash
を入れるのとアクションへのアクセスを独自関数&action/3
でやるのがミソです。標準で用意されているget
,post
,put
,delete
などを使うと conn が作り変えられてセッションが空マップ(%{})になります。ここは涙を飲んで換えて下さい。なあに、アクションもただの関数だからあんま変わりませんって。一応念の為にセッションが必要ないテストでは標準関数を使うことをおすすめします。
できた? できたね? 良かったですね。