gpt4 book ai didi

elixir - 在测试过程中丢失 conn.assigns

转载 作者:行者123 更新时间:2023-12-02 07:20:50 26 4
gpt4 key购买 nike

我很难对失败的测试进行故障排除,其中 conn.assigns 都是同一测试语句的一部分,但在测试的两行之间被清空。

我正在阅读“Programming Phoenix”,并重新编写代码以使其与 Phoenix 1.3 兼容。在其中 3 个测试中,存储在 conn.assigns 中的 :current_user 在测试过程中丢失,例如在 Controller 测试中的删除和获取之间丢失。

测试代码为:

  describe "delete video" do
setup [:login_user, :create_video]

test "deletes chosen video", %{conn: conn, video: video} do
conn = delete conn, video_path(conn, :delete, video)
assert redirected_to(conn) == video_path(conn, :index)
assert_error_sent 404, fn ->
Logger.warn("Before get: #{inspect(conn.assigns)}")
conn = get conn, video_path(conn, :show, video)
Logger.warn("After get: #{inspect(conn.assigns)}")
conn
end
end
end

我添加了 Logger.warn 指令来检查 get 请求之前和之后的连接。在“Before get:”日志中,我仍然有一个带有 :current_user 键的 conn.assigns,但在随后的“After get”日志中,它消失了。

我最初认为我的身份验证机制对此负责,但随后我创建了一个日志插件来检查管道,并且我可以看到从调用 get 的管道开始,分配时缺少 current_user。

这是测试输出,显示管道开始/结束时的日志记录以及上面测试的日志。

ubuntu@ubuntu-xenial:~/rumbl$ MIX_ENV=test mix test test/rumbl_web/controllers/video_controller_test.exs:98
[info] Already up
Including tags: [line: "98"]
Excluding tags: [:test]

warning: module attribute @update_attrs was set but never used
test/rumbl_web/controllers/video_controller_test.exs:10

[debug] QUERY OK db=1.2ms
begin []
[debug] QUERY OK db=2.7ms
INSERT INTO "users" ("name","password_hash","username","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" ["Some User", "$2b$12$0VvTPna8JMdSXcbNz2uvwOdhYWF/3ibhQ.gntdXqsb8v1TSeCZ0.K", "max", {{2017, 9, 22}, {10, 56, 53, 527902}}, {{2017, 9, 22}, {10, 56, 53, 529912}}]
[debug] QUERY OK db=0.1ms
commit []
[debug] QUERY OK db=0.1ms
begin []
[debug] QUERY OK db=1.1ms
INSERT INTO "videos" ("description","title","url","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6) RETURNING "id" ["some description", "some title", "some url", 351, {{2017, 9, 22}, {10, 56, 53, 544069}}, {{2017, 9, 22}, {10, 56, 53, 544075}}]
[debug] QUERY OK db=0.1ms
commit []
[info] DELETE /manage/videos/274
[warn] Start of pipeline: %{current_user: %RumblWeb.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 351, inserted_at: ~N[2017-09-22 10:56:53.527902], name: "Some User", password: "supersecret", password_hash: "$2b$12$0VvTPna8JMdSXcbNz2uvwOdhYWF/3ibhQ.gntdXqsb8v1TSeCZ0.K", updated_at: ~N[2017-09-22 10:56:53.529912], username: "max", videos: #Ecto.Association.NotLoaded<association :videos is not loaded>}}
[warn] End of pipeline: %{current_user: %RumblWeb.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 351, inserted_at: ~N[2017-09-22 10:56:53.527902], name: "Some User", password: "supersecret", password_hash: "$2b$12$0VvTPna8JMdSXcbNz2uvwOdhYWF/3ibhQ.gntdXqsb8v1TSeCZ0.K", updated_at: ~N[2017-09-22 10:56:53.529912], username: "max", videos: #Ecto.Association.NotLoaded<association :videos is not loaded>}}
[warn] Start of authenticate_user: %{current_user: %RumblWeb.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 351, inserted_at: ~N[2017-09-22 10:56:53.527902], name: "Some User", password: "supersecret", password_hash: "$2b$12$0VvTPna8JMdSXcbNz2uvwOdhYWF/3ibhQ.gntdXqsb8v1TSeCZ0.K", updated_at: ~N[2017-09-22 10:56:53.529912], username: "max", videos: #Ecto.Association.NotLoaded<association :videos is not loaded>}}
[debug] Processing with RumblWeb.VideoController.delete/2
Parameters: %{"id" => "274"}
Pipelines: [:browser, :authenticate_user]
[debug] QUERY OK source="videos" db=0.9ms
SELECT v0."id", v0."description", v0."title", v0."url", v0."user_id", v0."category_id", v0."inserted_at", v0."updated_at" FROM "videos" AS v0 WHERE (v0."user_id" = $1) AND (v0."id" = $2) [351, 274]
[debug] QUERY OK db=0.1ms
begin []
[debug] QUERY OK db=0.3ms
DELETE FROM "videos" WHERE "id" = $1 [274]
[debug] QUERY OK db=0.0ms
commit []
[info] Sent 302 in 46ms
[warn] Before get: %{current_user: %RumblWeb.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, id: 351, inserted_at: ~N[2017-09-22 10:56:53.527902], name: "Some User", password: "supersecret", password_hash: "$2b$12$0VvTPna8JMdSXcbNz2uvwOdhYWF/3ibhQ.gntdXqsb8v1TSeCZ0.K", updated_at: ~N[2017-09-22 10:56:53.529912], username: "max", videos: #Ecto.Association.NotLoaded<association :videos is not loaded>}}
[info] GET /manage/videos/274
[warn] Start of pipeline: %{}
[warn] End of pipeline: %{current_user: nil}
[warn] Start of authenticate_user: %{current_user: nil}
[info] Sent 302 in 2ms
[warn] After get: %{current_user: nil}


1) test delete video deletes chosen video (RumblWeb.VideoControllerTest)
test/rumbl_web/controllers/video_controller_test.exs:98
expected error to be sent as 404 status, but response sent 302 without error
code: assert_error_sent 404, fn ->
stacktrace:
(phoenix) lib/phoenix/test/conn_test.ex:600: Phoenix.ConnTest.assert_error_sent/2
test/rumbl_web/controllers/video_controller_test.exs:101: (test)



Finished in 0.6 seconds
8 tests, 1 failure, 7 skipped

Randomized with seed 982730

认证机制遵循本书的建议,如果 conn.assigns 中有 :current_user 则通过认证。设置 login_user 就是这样做的,在 :current_user 键下的 conn.assigns 中注入(inject)用户。在我的测试中,无论出于何种原因,由于 get 请求中的某个时刻 conn.assigns 为空,身份验证失败,用户被重定向到主页。

我不确定如何对删除语句和后续获取之间发生的情况进行故障排除。我查看了管道,但正如日志语句中所示, conn.assigns 在进入管道之前就被清空了。我预计 conn 在整个测试语句中不会改变,也许这是一个错误的假设。

回复Dogbert的评论:

Controller 测试中的login_user函数:

  defp login_user(_) do
user = insert_user(username: "max")
conn = assign(build_conn(), :current_user, user)
{:ok, conn: conn, user: user}
end

在auth Controller 中调用函数:

  def call(conn, repo) do
user_id = get_session(conn, :user_id)

cond do
user = conn.assigns[:current_user] ->
conn
user = user_id && repo.get(RumblWeb.User, user_id) ->
assign(conn, :current_user, user)
true ->
assign(conn, :current_user, nil)
end
end

最佳答案

这是由phoenix回收引起的,参见( https://hexdocs.pm/phoenix/1.3.0/Phoenix.ConnTest.html#module-recycling )

回收

浏览器通过使用cookie来实现存储。当在响应中设置 cookie 时,浏览器会存储它并在下一个请求中发送它。

为了模拟这种行为,该模块提供了回收的想法。 recycle/1 函数接收一个连接并返回一个新连接,类似于 conn/0 返回的连接,并将前一个连接的所有响应 cookie 定义为请求 header 。这在测试需要 cookie 或 session 才能工作的多个路由时非常有用。

请记住,Phoenix 会自动回收调度之间的连接。大多数情况下这通常效果很好,但如果您在下一次调度之前修改连接,它可能会丢弃信息:

# No recycling as the connection is fresh
conn = get build_conn(), "/"

# The connection is recycled, creating a new one behind the scenes
conn = post conn, "/login"

# We can also recycle manually in case we want custom headers
conn =
conn
|> recycle()
|> put_req_header("x-special", "nice")

# No recycling as we did it explicitly
conn = delete conn, "/logout"

回收还会回收“accept” header 。

要保留分配,您可以在调用deleteget之间手动回收和分配:

saved_assigns = conn.assigns
conn =
conn
|> recycle()
|> Map.put(:assigns, saved_assigns)

关于elixir - 在测试过程中丢失 conn.assigns,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46363292/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com