You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elixir/Phoenix GraphQL订阅测试ChannelCase配置报错求助

问题解决:Elixir GraphQL订阅测试中Phoenix.ChannelTest.connect宏错误

问题背景

Elixir项目中实现的created_user GraphQL订阅在localhost:4000/graphiql运行正常,但编写单元测试时,自定义的ChannelCase和SubscriptionCase导致测试抛出UndefinedFunctionError,提示Phoenix.ChannelTest.connect/2是宏需require,尝试替换为import仍无效。

错误原因

  1. 宏调用上下文错误:Phoenix.ChannelTest.connect/2是宏,ChannelCase的setup块直接在模块上下文调用该宏,但未在ChannelCase模块内require对应宏模块。
  2. SubscriptionCase重复配置:SubscriptionCase中同时use ChannelCase又自行定义setup,不仅冗余还出现参数错误(错误传递Endpoint给connect,应该传UserSocket)。
  3. 依赖注入重复:重复导入/使用Phoenix.ChannelTest和Absinthe.Phoenix.SubscriptionTest,导致上下文混乱。

修复方案

1. 修正ChannelCase配置

在ChannelCase模块内require Phoenix.ChannelTest,确保宏能被正确调用,同时保留原有沙箱配置:

defmodule GraphqlUserWeb.ChannelCase do
  use ExUnit.CaseTemplate
  # 在此处require宏模块,确保setup块能调用connect宏
  require Phoenix.ChannelTest

  using do
    quote do
      use Phoenix.ChannelTest
      import Absinthe.Phoenix.SubscriptionTest

      @endpoint GraphqlUserWeb.Endpoint
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(GraphqlUser.Repo)

    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(GraphqlUser.Repo, {:shared, self()})
    end

    # 直接调用宏,此时已require
    {:ok, socket} = Phoenix.ChannelTest.connect(GraphqlUserWeb.UserSocket, %{})
    {:ok, socket: Absinthe.Phoenix.SubscriptionTest.join_absinthe(socket)}
  end
end

2. 简化SubscriptionCase配置

移除重复的setup和冗余导入,直接复用ChannelCase的配置:

defmodule GraphqlUserWeb.SubscriptionCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      use GraphqlUserWeb.ChannelCase
      use Absinthe.Phoenix.SubscriptionTest, schema: GraphqlUserWeb.Schema
      # 无需重复定义setup,ChannelCase已提供socket
    end
  end
end

3. 调整测试代码(优化验证逻辑)

确保测试逻辑完整,订阅后触发事件验证接收:

defmodule GraphqlUserWeb.SubscriptionTest do
  use GraphqlUserWeb.SubscriptionCase

  alias GraphqlUser.Factory

  describe "user subscriptions" do
    test "receives a created_user subscription event", %{socket: socket} do
      # 订阅createdUser
      ref = push_doc(socket, """
      subscription {
        createdUser {
          id
          name
          email
        }
      }
      """)
      assert_reply ref, :ok, %{subscriptionId: _subscription_id}

      # 创建用户触发订阅事件
      user = Factory.insert(:user)
      Absinthe.Subscription.publish(GraphqlUserWeb.Endpoint, user, created_user: "new_user")

      # 验证接收事件
      assert_push "subscription:data", %{result: %{data: %{"createdUser" => received_user}}}
      assert received_user["id"] == to_string(user.id)
      assert received_user["name"] == user.name
      assert received_user["email"] == user.email
    end
  end
end

关键说明

  • 宏必须在调用它的上下文被require,using块中的导入只作用于测试模块,不作用于ChannelCase自身的setup块。
  • SubscriptionCase无需重复实现setup,复用ChannelCase的配置即可避免冗余和错误。
  • 测试中需主动触发订阅事件(通过Absinthe.Subscription.publish),才能验证订阅是否正常接收消息。

内容的提问来源于stack exchange,提问作者jl118

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:05:18