Prevent "GenServer terminating" error in exunit test with supervised process

950 views Asked by At

I have a test in my Phoenix app that is testing a Phoenix.PubSub subscriber that uses Genserver. The subscriber does some database work as part of its handle_info/2.

test "sending creating a referral code upon user registration" do
  start_supervised(MyApp.Accounts.Subscriber)
  user = insert(:user)

  Phoenix.PubSub.broadcast(MappApp.PubSub, "accounts", {:register, user})

  assert_eventually(Repo.exists?(ReferralCode))

  stop_supervised(MyApp.Accounts.Subscriber)
end

Running this test module by itself is fine. However when I run my entire test suite I get an error like so (the test still passes):

[error] GenServer MyApp.Accounts.Subscriber terminating
** (stop) exited in: DBConnection.Holder.checkout(#PID<0.970.0>, [log: #Function<9.124843621/1 in Ecto.Adapters.SQL.with_log/3>, cache_statement: "ecto_insert_referral_codes", timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
    ** (EXIT) shutdown: "owner #PID<0.969.0> exited"
    <stacktrace...>

This looks like it's an issue with the database connection still being open when the process is terminated so it doesn't die gracefully. But I'm not sure how to deal with this.

Any advice on how to prevent this error?

1

There are 1 answers

8
Everett On

I ran into this today. Any time you are doing database operations in separate child processes (e.g. database operations triggered inside a GenStage or GenServer et al), then you need to read the Sandbox adapter documentation carefully. There is an FAQ that deals with this error specifically, and the solution can be either to explicitly grant the Sandbox adapter access to the child process like this (from the docs):

test "gets results from GenServer" do
  {:ok, pid} = MyAppServer.start_link()
  Ecto.Adapters.SQL.Sandbox.allow(Repo, self(), pid)
  assert MyAppServer.get_my_data_fast(timeout: 1000) == [...]
end

or you can enabled "shared" mode by altering your test setup to set the Sandbox mode:

  setup do
    :ok = Sandbox.checkout(Repo)
    Sandbox.mode(Repo, {:shared, self()})
  end

I had more luck with the latter, but be aware that if you are using some other adapter anywhere explicitly (e.g. to make raw database calls), then it might cause tests to fail (because it can no longer get a connection).