在对自定义插件进行单元测试时访问会话和闪存
Accessing session and flash when unit-testing a custom plug
我有一个登录要求插件,它与 this example by Thoughtbot 中描述的插件非常相似。我想在重定向用户时添加一个 flash 通知。它在浏览器中有效,但在单独测试时无效。
插头:
# In webs/plugs/require_login.ex
defmodule MyApp.Plugs.RequireLogin do
import Plug.Conn
def init(opts), do: opts
if false # real user authentication omitted
conn
else
conn
|> Phoenix.Controller.put_flash(:error, "Login required.")
|> Phoenix.Controller.redirect(to: "/")
|> halt
end
end
end
用于此的测试:
defmodule MyApp.Plugs.RequireLoginTest do
use MyApp.ConnCase
test "user is redirected when authentication fails" do
conn = conn |> MyApp.Plugs.RequireLogin.call(%{})
assert Phoenix.Controller.get_flash(conn, :error) == "Login required."
assert redirected_to(conn) == "/"
end
end
我收到的错误信息是:
(ArgumentError) flash not fetched, call fetch_flash/2
错误发生在插件模块中,但如果我在那里注释掉 put_session
行,错误就会转移到我的测试文件中。
我知道会话存储是在 lib/my_app/endpoint.ex
中配置的,但是我怎样才能重新使用这个配置以便我可以对插件进行单元测试?
插头连接路由器的方式如下:
# web/router.ex
pipeline :browser do
# the Phoenix default
end
scope "/", MyApp do
pipe_through [:browser, MyApp.Plugs.RequireLogin]
resource "/protected", MyController, only: [:index]
end
编辑
我首先想到的是您在测试中缺少参数。
您的 conn 可能是在您的 ConnCase 中某处生成的:
setup tags do
...
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
你的测试应该是这样的:
test "user is redirected when authentication fails", %{conn: conn} do
能够使用这个conn。
第二件事是你的测试中没有请求(get/post 等),你的连接可能没有通过 :browser
管道。
我使用 bypass_through 解决了这个问题。我没有 "fire" 使用例如Phoenix.ConnTest.get/3
,导致插件管道没有被调用。我一添加 get/3
,闪存哈希就可用了。
defmodule MyApp.Plugs.RequireLoginTest do
use MyApp.ConnCase
test "user is redirected when authentication fails", %{conn: conn} do
conn = conn
|> with_pipeline
|> MyApp.Plugs.RequireLogin.call(%{})
assert Phoenix.Controller.get_flash(conn, :error) == "Login required."
assert redirected_to(conn) == "/"
end
defp with_pipeline(conn) do
conn
|> bypass_through(MyApp.Router, [:browser])
|> get("/protected-uri") # any uri, bypass_through ignores the router rules
end
end
我有一个登录要求插件,它与 this example by Thoughtbot 中描述的插件非常相似。我想在重定向用户时添加一个 flash 通知。它在浏览器中有效,但在单独测试时无效。
插头:
# In webs/plugs/require_login.ex
defmodule MyApp.Plugs.RequireLogin do
import Plug.Conn
def init(opts), do: opts
if false # real user authentication omitted
conn
else
conn
|> Phoenix.Controller.put_flash(:error, "Login required.")
|> Phoenix.Controller.redirect(to: "/")
|> halt
end
end
end
用于此的测试:
defmodule MyApp.Plugs.RequireLoginTest do
use MyApp.ConnCase
test "user is redirected when authentication fails" do
conn = conn |> MyApp.Plugs.RequireLogin.call(%{})
assert Phoenix.Controller.get_flash(conn, :error) == "Login required."
assert redirected_to(conn) == "/"
end
end
我收到的错误信息是:
(ArgumentError) flash not fetched, call fetch_flash/2
错误发生在插件模块中,但如果我在那里注释掉 put_session
行,错误就会转移到我的测试文件中。
我知道会话存储是在 lib/my_app/endpoint.ex
中配置的,但是我怎样才能重新使用这个配置以便我可以对插件进行单元测试?
插头连接路由器的方式如下:
# web/router.ex
pipeline :browser do
# the Phoenix default
end
scope "/", MyApp do
pipe_through [:browser, MyApp.Plugs.RequireLogin]
resource "/protected", MyController, only: [:index]
end
编辑
我首先想到的是您在测试中缺少参数。
您的 conn 可能是在您的 ConnCase 中某处生成的:
setup tags do
...
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
你的测试应该是这样的:
test "user is redirected when authentication fails", %{conn: conn} do
能够使用这个conn。
第二件事是你的测试中没有请求(get/post 等),你的连接可能没有通过 :browser
管道。
我使用 bypass_through 解决了这个问题。我没有 "fire" 使用例如Phoenix.ConnTest.get/3
,导致插件管道没有被调用。我一添加 get/3
,闪存哈希就可用了。
defmodule MyApp.Plugs.RequireLoginTest do
use MyApp.ConnCase
test "user is redirected when authentication fails", %{conn: conn} do
conn = conn
|> with_pipeline
|> MyApp.Plugs.RequireLogin.call(%{})
assert Phoenix.Controller.get_flash(conn, :error) == "Login required."
assert redirected_to(conn) == "/"
end
defp with_pipeline(conn) do
conn
|> bypass_through(MyApp.Router, [:browser])
|> get("/protected-uri") # any uri, bypass_through ignores the router rules
end
end