当我在会话中测试需要 user_id 的凤凰动作时,如何在设置中设置会话?
how can i set session in setup when i test phoenix action which need user_id in session?
我有一个测试需要在测试前将 user_id 设置为会话,因为此操作需要知道 current_user.
setup do
%User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert
{:ok, user: Repo.get(User, 123456) }
end
test "POST /posts", context do
# conn = conn()
# |> put_session(:user_id, context[:user].id)
# |> post("/posts", %{ post: %{ title: "title", body: "body" } })
# assert get_flash(conn, :info) == "Post created successfully."
# updated to =>
conn = conn()
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Plug.Conn.fetch_session
|> put_session(:user_id, context[:user].id)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
end
我试过这段代码,但它说 session not fetched, call fetch_session/2
.
web/controllers/controller_helper.ex
defmodule SimpleBlog.ControllerHelpers do
alias Phoenix.Controller
alias Plug.Conn
alias SimpleBlog.Router.Helpers
def authenticate(conn, _) do
case Conn.get_session(conn, :user_id) do
nil ->
unauthorized(conn)
user_id ->
case SimpleBlog.Repo.get(SimpleBlog.User, user_id) do
{:ok, user} ->
Conn.assign(conn, :current_user, user)
nil ->
unauthorized(conn)
end
end
end
def unauthorized(conn) do
conn
|> Controller.put_flash(:error, "You must be logged in")
|> Controller.redirect(to: Helpers.session_path(conn, :new))
|> Conn.halt
end
end
已更新
当我通过 Conn.get_session(conn, :user_id)
从会话中获得 user_id 时,我得到零。
这里是post控制器
web/controllers/post_controller.ex
defmodule SimpleBlog.PostController do
use SimpleBlog.Web, :controller
import SimpleBlog.ControllerHelpers
alias SimpleBlog.Post
plug :authenticate when not action in [:new]
def create(conn, %{ "post" => post_params }) do
changeset = Post.changeset(%Post{}, post_params)
case Repo.insert(changeset) do
{:ok, _post} ->
conn
|> put_flash(:info, "Post created successfully.")
|> redirect(to: post_path(conn, :new))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
end
这是我的测试文件。
defmodule SimpleBlog.PostControllerTest do
use SimpleBlog.ConnCase
alias SimpleBlog.Repo
alias SimpleBlog.User
@session Plug.Session.init(
store: :cookie,
key: "_app",
encryption_salt: "yadayada",
signing_salt: "yadayada"
)
setup do
%User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert
{:ok, user: Repo.get(User, 123456) }
end
@tag timeout: 900000
test "POST /posts", context do
conn = conn()
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Plug.Conn.fetch_session
|> put_session(:user_id, context[:user].id)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
end
end
更新..
lib/simple_blog/plugs/authenticated.ex
我定义了一个经过认证的插件
defmodule SimpleBlog.Plugs.Authenticated do
import Plug.Conn
alias Phoenix.Controller
alias SimpleBlog.Router.Helpers
alias SimpleBlog.User
def init(options) do
options
end
def call(conn, _) do
case conn |> current_user_id do
nil ->
conn
|> Controller.put_flash(:error, "You must be logged in")
|> Controller.redirect(to: Helpers.session_path(conn, :new))
|> halt
current_user_id ->
conn |> assign(:current_user, SimpleBlog.Repo.get(User, current_user_id))
end
end
defp current_user_id(conn) do
case Mix.env do
:test ->
conn.private[:authenticated_current_user_id]
_ ->
conn |> fetch_session |> get_session(:current_user_id)
end
end
end
在我的测试中
conn = conn()
|> put_private(:authenticated_current_user_id, context[:user].id)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
现在,测试通过了。
由于 post
重置会话的操作,您实际上不能这样做。你有几个选择。
首先,您可以进行集成测试,使用有效凭据访问您的登录路径,然后请求创建 post。
其次,您可以像这样创建一个身份验证插件:
defmodule SimpleBlog.Plug.Authenticate do
import Plug.Conn
alias SimpleBlog.Router.Helpers, as: RouteHelpers
import Phoenix.Controller
alias SimpleBlog.Repo
alias SimpleBlog.User
def init(opts), do: opts
def call(conn, _opts) do
if user = get_user(conn) do
assign(conn, :current_user, user)
else
auth_error!(conn)
end
end
def get_user(conn) do
case conn.assigns[:current_user] do
nil -> fetch_user(conn)
user -> user
end
end
defp fetch_user(conn) do
case get_session(conn, :current_user) |> find_user
{:ok, user} -> user
_ -> nil
end
end
defp find_user(id) when do
Repo.get(User, id)
end
defp auth_error!(conn) do
conn
|> put_flash(:error, "You need to be signed in to view this page")
|> redirect(to: RouteHelpers.session_path(conn, :new))
|> halt
end
end
您可以通过执行以下摘自 Phoenix tests 的操作来测试它:
defmodule SimpleBlog.Plug.AuthenticationTest do
use ExUnit.Case
use Plug.Test
alias Plug.Conn
alias SimpleBlog.Plug.Authenticate
alias SimpleBlog.Repo
alias SimpleBlog.User
import SimpleBlog.Router.Helpers
@session Plug.Session.init(
store: :cookie,
key: "_app",
encryption_salt: "yadayada",
signing_salt: "yadayada"
)
setup do
user = %User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert!
session_data = %{id: user.id}
conn =
conn(:get, "/")
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Conn.fetch_session()
{:ok, conn: conn, user: user, session_data: session_data}
end
test "get_user returns the user if it is set in conn.assigns", %{conn: conn, user: user} do
conn = Conn.assign(conn, :current_user, user)
assert Authenticate.get_user(conn) == user
end
test "get_user returns the user if it is set in a session", %{conn: conn, user: user, session_data: session_data} do
conn = Conn.put_session(conn, :current_user, session_data)
assert Authenticate.get_user(conn) == user
end
test "get_user returns nil if the user is not in assigns or session", %{conn: conn} do
assert Authenticate.get_user(conn) == nil
end
test "when there is not user stored", %{conn: conn} do
conn =
|> Phoenix.Controller.fetch_flash
|> Authenticate.call([])
assert Phoenix.Controller.get_flash(new_conn, :error) == "You need to be signed in to view this page"
assert Phoenix.ConnTest.redirected_to(new_conn) == session_path(new_conn, :new)
end
end
您现在可以通过以下方式测试您的控制器:
setup do
%User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert
{:ok, user: Repo.get(User, 123456) }
end
test "POST /posts", %{user: user} do
conn = conn()
|> assign(:current_user, user)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
end
另一种简单的方法是使用assigns
,并从会话中延迟加载数据。在集成测试期间,会话中的数据将被加载,但在单元测试期间,您可以简单地分配数据:
# controller code
def success(conn, _params) do
conn
|> assign(:username, conn.assigns[:username] || get_session(conn, :username))
|> render("auth_success.html")
end
# test
test "it renders success page with username and token", %{conn: conn} do
conn = conn
|> assign(:username, "unpredictablestring")
|> get("/portal/success")
assert html_response(conn, 200) =~ "unpredictablestring"
end
信用来自评论 ,我只是认为这应该作为答案发布。
Phoenix.ConnTest.init_test_session/2
可能有助于在测试中设置会话。如果例如您需要 :token
出现在会话中 您可以这样设置它:
conn =
conn
|> init_test_session(conn, %{token: "some-token"})
|> get("/some-request")
我有一个测试需要在测试前将 user_id 设置为会话,因为此操作需要知道 current_user.
setup do
%User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert
{:ok, user: Repo.get(User, 123456) }
end
test "POST /posts", context do
# conn = conn()
# |> put_session(:user_id, context[:user].id)
# |> post("/posts", %{ post: %{ title: "title", body: "body" } })
# assert get_flash(conn, :info) == "Post created successfully."
# updated to =>
conn = conn()
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Plug.Conn.fetch_session
|> put_session(:user_id, context[:user].id)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
end
我试过这段代码,但它说 session not fetched, call fetch_session/2
.
web/controllers/controller_helper.ex
defmodule SimpleBlog.ControllerHelpers do
alias Phoenix.Controller
alias Plug.Conn
alias SimpleBlog.Router.Helpers
def authenticate(conn, _) do
case Conn.get_session(conn, :user_id) do
nil ->
unauthorized(conn)
user_id ->
case SimpleBlog.Repo.get(SimpleBlog.User, user_id) do
{:ok, user} ->
Conn.assign(conn, :current_user, user)
nil ->
unauthorized(conn)
end
end
end
def unauthorized(conn) do
conn
|> Controller.put_flash(:error, "You must be logged in")
|> Controller.redirect(to: Helpers.session_path(conn, :new))
|> Conn.halt
end
end
已更新
当我通过 Conn.get_session(conn, :user_id)
从会话中获得 user_id 时,我得到零。
这里是post控制器 web/controllers/post_controller.ex
defmodule SimpleBlog.PostController do
use SimpleBlog.Web, :controller
import SimpleBlog.ControllerHelpers
alias SimpleBlog.Post
plug :authenticate when not action in [:new]
def create(conn, %{ "post" => post_params }) do
changeset = Post.changeset(%Post{}, post_params)
case Repo.insert(changeset) do
{:ok, _post} ->
conn
|> put_flash(:info, "Post created successfully.")
|> redirect(to: post_path(conn, :new))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
end
这是我的测试文件。
defmodule SimpleBlog.PostControllerTest do
use SimpleBlog.ConnCase
alias SimpleBlog.Repo
alias SimpleBlog.User
@session Plug.Session.init(
store: :cookie,
key: "_app",
encryption_salt: "yadayada",
signing_salt: "yadayada"
)
setup do
%User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert
{:ok, user: Repo.get(User, 123456) }
end
@tag timeout: 900000
test "POST /posts", context do
conn = conn()
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Plug.Conn.fetch_session
|> put_session(:user_id, context[:user].id)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
end
end
更新..
lib/simple_blog/plugs/authenticated.ex
我定义了一个经过认证的插件
defmodule SimpleBlog.Plugs.Authenticated do
import Plug.Conn
alias Phoenix.Controller
alias SimpleBlog.Router.Helpers
alias SimpleBlog.User
def init(options) do
options
end
def call(conn, _) do
case conn |> current_user_id do
nil ->
conn
|> Controller.put_flash(:error, "You must be logged in")
|> Controller.redirect(to: Helpers.session_path(conn, :new))
|> halt
current_user_id ->
conn |> assign(:current_user, SimpleBlog.Repo.get(User, current_user_id))
end
end
defp current_user_id(conn) do
case Mix.env do
:test ->
conn.private[:authenticated_current_user_id]
_ ->
conn |> fetch_session |> get_session(:current_user_id)
end
end
end
在我的测试中
conn = conn()
|> put_private(:authenticated_current_user_id, context[:user].id)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
现在,测试通过了。
由于 post
重置会话的操作,您实际上不能这样做。你有几个选择。
首先,您可以进行集成测试,使用有效凭据访问您的登录路径,然后请求创建 post。
其次,您可以像这样创建一个身份验证插件:
defmodule SimpleBlog.Plug.Authenticate do
import Plug.Conn
alias SimpleBlog.Router.Helpers, as: RouteHelpers
import Phoenix.Controller
alias SimpleBlog.Repo
alias SimpleBlog.User
def init(opts), do: opts
def call(conn, _opts) do
if user = get_user(conn) do
assign(conn, :current_user, user)
else
auth_error!(conn)
end
end
def get_user(conn) do
case conn.assigns[:current_user] do
nil -> fetch_user(conn)
user -> user
end
end
defp fetch_user(conn) do
case get_session(conn, :current_user) |> find_user
{:ok, user} -> user
_ -> nil
end
end
defp find_user(id) when do
Repo.get(User, id)
end
defp auth_error!(conn) do
conn
|> put_flash(:error, "You need to be signed in to view this page")
|> redirect(to: RouteHelpers.session_path(conn, :new))
|> halt
end
end
您可以通过执行以下摘自 Phoenix tests 的操作来测试它:
defmodule SimpleBlog.Plug.AuthenticationTest do
use ExUnit.Case
use Plug.Test
alias Plug.Conn
alias SimpleBlog.Plug.Authenticate
alias SimpleBlog.Repo
alias SimpleBlog.User
import SimpleBlog.Router.Helpers
@session Plug.Session.init(
store: :cookie,
key: "_app",
encryption_salt: "yadayada",
signing_salt: "yadayada"
)
setup do
user = %User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert!
session_data = %{id: user.id}
conn =
conn(:get, "/")
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8))
|> Plug.Session.call(@session)
|> Conn.fetch_session()
{:ok, conn: conn, user: user, session_data: session_data}
end
test "get_user returns the user if it is set in conn.assigns", %{conn: conn, user: user} do
conn = Conn.assign(conn, :current_user, user)
assert Authenticate.get_user(conn) == user
end
test "get_user returns the user if it is set in a session", %{conn: conn, user: user, session_data: session_data} do
conn = Conn.put_session(conn, :current_user, session_data)
assert Authenticate.get_user(conn) == user
end
test "get_user returns nil if the user is not in assigns or session", %{conn: conn} do
assert Authenticate.get_user(conn) == nil
end
test "when there is not user stored", %{conn: conn} do
conn =
|> Phoenix.Controller.fetch_flash
|> Authenticate.call([])
assert Phoenix.Controller.get_flash(new_conn, :error) == "You need to be signed in to view this page"
assert Phoenix.ConnTest.redirected_to(new_conn) == session_path(new_conn, :new)
end
end
您现在可以通过以下方式测试您的控制器:
setup do
%User{
id: 123456,
username: "lcp",
email: "abc@gmail.com",
password: Comeonin.Bcrypt.hashpwsalt("password")
} |> Repo.insert
{:ok, user: Repo.get(User, 123456) }
end
test "POST /posts", %{user: user} do
conn = conn()
|> assign(:current_user, user)
|> post("/posts", %{ post: %{ title: "title", body: "body" } })
assert get_flash(conn, :info) == "Post created successfully."
end
另一种简单的方法是使用assigns
,并从会话中延迟加载数据。在集成测试期间,会话中的数据将被加载,但在单元测试期间,您可以简单地分配数据:
# controller code
def success(conn, _params) do
conn
|> assign(:username, conn.assigns[:username] || get_session(conn, :username))
|> render("auth_success.html")
end
# test
test "it renders success page with username and token", %{conn: conn} do
conn = conn
|> assign(:username, "unpredictablestring")
|> get("/portal/success")
assert html_response(conn, 200) =~ "unpredictablestring"
end
信用来自评论
Phoenix.ConnTest.init_test_session/2
可能有助于在测试中设置会话。如果例如您需要 :token
出现在会话中 您可以这样设置它:
conn =
conn
|> init_test_session(conn, %{token: "some-token"})
|> get("/some-request")