graphqlerlangelixirphoenix-frameworkchannel

Elixir/Phoenix Testing Graphql Subscription ChannelCase and SubscriptionCase Set Up


For my elixir project I have a following subscription in my lib/graphql_user_web/schema/subscriptions/user.ex:

defmodule GraphqlUserWeb.Schema.Subscriptions.User do
  use Absinthe.Schema.Notation

  object :user_subscriptions do
    field :created_user, :user do
      trigger :create_user, topic: fn _ ->
        "new_user"
      end

      config fn _, _ ->
        {:ok, topic: "new_user"}
      end
    end
  end
end

So far it has been working well on localhost:4000/graphiql.

I just want to add unit test for it.

Here's my setup of ChannelCase in test/support/channel_case.ex:

defmodule GraphqlUserWeb.ChannelCase do
  use ExUnit.CaseTemplate

  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

    {:ok, socket} = Phoenix.ChannelTest.connect(GraphqlUserWeb.UserSocket, %{})
    {:ok, socket: Absinthe.Phoenix.SubscriptionTest.join_absinthe(socket)}
  end
end

And here's my SubscriptionCase setup in test/support/subscription_case.ex:

defmodule GraphqlUserWeb.SubscriptionCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      import Phoenix.ChannelTest
      use GraphqlUserWeb.ChannelCase
      use Absinthe.Phoenix.SubscriptionTest,
       schema: GraphqlUserWeb.Schema
      setup tags do
        {:ok, socket} = Phoenix.ChannelTest.connect(GraphqlUserWeb.Endpoint, %{})
        {:ok, socket: Absinthe.Phoenix.SubscriptionTest.join_absinthe(socket)}
        {:ok, %{socket: socket}}
      end
    end
  end
end

And I have a pretty simple/basic test in test/graphql_user_web/schema/subscriptions/user_test.ex:

defmodule GraphqlUserWeb.SubscriptionTest do
  use GraphqlUserWeb.SubscriptionCase

  alias GraphqlUser.Factory

  describe "user subscriptions" do
    setup do
      {:ok, user: Factory.insert(:user)}
    end

    test "receives a created_user subscription event", %{socket: socket} do
      # Subscribe to the "created_user" subscription
      ref = push_doc(socket, """
      subscription {
        createdUser {
          id
          name
          email
        }
      }
      """)
      assert_reply ref, :ok, reply
      IO.inspect(reply)
    end
  end
end

The test basically cannot run and throws error:

  1) test user subscriptions receives a created_user subscription event (GraphqlUserWeb.SubscriptionTest)
     test/graphql_user_web/schema/subscriptions/user_test.exs:11
     ** (UndefinedFunctionError) function Phoenix.ChannelTest.connect/2 is undefined or private. However, there is a macro with the same name and arity. Be sure to require Phoenix.ChannelTest if you intend to invoke this macro
     stacktrace:
       (phoenix 1.7.18) Phoenix.ChannelTest.connect(GraphqlUserWeb.UserSocket, %{})
       (graphql_user 0.1.0) test/support/channel_case.ex:20: GraphqlUserWeb.ChannelCase.__ex_unit_setup_0/1
       (graphql_user 0.1.0) test/support/channel_case.ex:1: GraphqlUserWeb.ChannelCase.__ex_unit__/2
       test/graphql_user_web/schema/subscriptions/user_test.exs:1: GraphqlUserWeb.SubscriptionTest.__ex_unit__/2

I tried doing import Phoenix.ChannelTest instead of use Phoenix.ChannelTest. Still same error. Can anyone point out where I'm being dumb? Thanks! I think it's my setup of ChannelCase and/or SubscriptionCase.


Solution

  • TLDR: you should add the following:

    defmodule GraphqlUserWeb.ChannelCase do
      use ExUnit.CaseTemplate
      
      require Phoenix.ChannelTest # <= here
    
      using do
    

    As for the longer explanation, there are two different lexical scopes at work here:

    use is for injecting code in other modules, but it won't affect the module defining the code being injected.