gpt4 book ai didi

elixir - 使用 GenServer.cast 的测试代码

转载 作者:行者123 更新时间:2023-12-01 07:45:17 26 4
gpt4 key购买 nike

我有一个模拟应用程序范围的 Repo 的测试。大多数时候,测试都是绿色的。当我在循环中运行测试时,使用相同的种子,可能有 10% 的运行成功,但带有 GenServer 终止消息:

15:39:34.632 [error] GenServer ShortTermMessageStore terminating
** (CaseClauseError) no case clause matching: {:error, %Postgrex.Error{connection_id: 11136, message: nil, postgres: %{code: :foreign_key_violation, constraint: "chat_messages_room_id_ref_fkey", detail: "Key (room_id_ref)=(c78940ab-2514-493e-81fe-64efc63c7bb0) is not present in table \"chat_rooms\".", file: "ri_triggers.c", line: "3324", message: "insert or update on table \"chat_messages\" violates foreign key constraint \"chat_messages_room_id_ref_fkey\"", pg_code: "23503", routine: "ri_ReportViolation", schema: "public", severity: "ERROR", table: "chat_messages", unknown: "ERROR"}}}
(chat_web) lib/chat_web/repo.ex:78: ChatWeb.Repo.append_message_to_store/3
(chat_web) lib/short_term_message_store.ex:23: ChatWeb.ShortTermMessageStore.handle_cast/2
(stdlib) gen_server.erl:601: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:667: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:published, "USERID", "c78940ab-2514-493e-81fe-64efc63c7bb0", %{"id" => "123", "room_id" => "c78940ab-2514-493e-81fe-64efc63c7bb0"}}}

我相信我已将问题追溯到单个测试。当我注释掉该测试时,我可以一次运行测试几分钟而不会看到任何警告。

有问题的代码是:

test_with_mock "publish_message appends message to room", Repo, [], [append_message_to_store: fn(_, _, _) -> true end] do
room_id = "c78940ab-2514-493e-81fe-64efc63c7bb0"
{:ok, _room} = open_room room_id
sock = open_socket()
|> subscribe_and_join!(RoomChannel, "room:#{room_id}")

push sock, "publish_message", %{"id" => "123", "room_id" => room_id}

assert_broadcast "publish_message", %{"id" => "123", "room_id" => room_id, "sequence" => 1}
{:ok, mailbox} = Rooms.mailbox(room_id)
assert [%{"id" => "123", "room_id" => "c78940ab-2514-493e-81fe-64efc63c7bb0", "sequence" => 1}] = mailbox
end

defmodule RoomChannel do
def handle_in("publish_message", payload, socket) do
{:ok, message} = Rooms.publish(payload["room_id"], payload)
broadcast(socket, "publish_message", message)

ShortTermMessageStore.publish(socket.assigns[:user_id], payload["room_id"], payload)

{:reply, {:ok, %{payload: payload}}, socket}
end
end

defmodule ShortTermMessageStore do
def publish(user_id, room_id, message) do
GenServer.cast(ShortTermMessageStore.address, {:published, user_id, room_id, message})
end

def handle_cast({:published, user_id, room_id, message}, state) do
Logger.debug fn -> "STMS: #{inspect(message)}" end
Repo.append_message_to_store(user_id, room_id, message)
{:noreply, state}
end
end

代码流程是:调用RoomChannelhandle_in({:publish_message})。这会调用业务逻辑,然后调用 ShortTermMessageStore。这会执行 GenServer.cast,然后返回。测试运行从它停止的地方开始,测试和模拟被拆除。我相信在某些情况下,测试和模拟被过早地拆除:cast 还没有开始,Repo 的真正实现已经运行。这会生成我在上面粘贴的消息,我们尝试运行 SQL INSERT 语句,但它失败了,因为尚未为外键设置数据库。

我知道根据设计,转换不应该支持回复。为了我的测试,我能提供什么帮助?我需要在回来之前 sleep 吗?这看起来很粗俗和不雅。

我找到了 What is the idiomatic testing strategy for GenServers in Elixir?它有很好的元素,但就我而言,我没有测试异步对象。这只是调用另一个函数的副作用。

我还找到了Testing asynchronous code in Elixir这让我觉得我可能需要监控一些东西。

最佳答案

您可以通过调用 Pid 上的 :sys.get_state/1 等到 GenServer 处理完所有未决消息。 :sys.get_state/1 对 GenServer 进行同步调用以获取其状态,该状态在所有未决消息已由其处理后完成。

因此,如果您将以下代码添加到测试的末尾,您的测试将一直等到 ShortTermMessageStore.address 处理完它已收到的所有消息。

:sys.get_state(ShortTermMessageStore.address)

关于elixir - 使用 GenServer.cast 的测试代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43725934/

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