Предотвращение ошибки «Завершение работы GenServer» в тесте exunit с контролируемым процессом

#elixir #gen-server #ex-unit

#elixir #gen-server #ex-unit

Вопрос:

У меня есть тест в моем приложении Phoenix, который тестирует Phoenix.PubSub подписчика, который использует Genserver. Подписчик выполняет некоторую работу с базой данных в рамках 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
 

Запуск этого тестового модуля сам по себе прекрасен. Однако, когда я запускаю весь свой набор тестов, я получаю сообщение об ошибке, подобное so (тест все еще проходит):

 [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...>
 

Похоже, что это проблема с подключением к базе данных, которое все еще открыто, когда процесс завершается, поэтому он не завершается корректно. Но я не уверен, как с этим справиться.

Любые советы о том, как предотвратить эту ошибку?

Комментарии:

1. Вы случайно не запускаете этот тестовый модуль с async: true ? Если это так, это может быть виновником.

2. Я use редактирую ModelCase модуль. Это поведение по умолчанию?

3. Если у вас просто есть use ModelCase (и нет use ModelCase, async: true ), то тесты в модуле не будут выполняться одновременно с тестами в других модулях, что, вероятно, то, что вы хотите. (Хотя я не знаю, как выглядит ваш ModelCase)

4. Это в основном стандартный (теперь вызывается DataCase в текущем Phoenix: github.com/phoenixframework/phoenix/blob/master/installer /… ).

5. Я пробовал с и без async: false , и возникает та же ошибка. Я думаю ModelCase , что тест выполняется не асинхронно

Ответ №1:

Я столкнулся с этим сегодня. Каждый раз, когда вы выполняете операции с базой данных в отдельных дочерних процессах (например, операции с базой данных, запускаемые внутри GenStage или GenServer и др.), Вам необходимо внимательно прочитать документацию по адаптеру изолированной среды. Существует FAQ, в котором конкретно рассматривается эта ошибка, и решением может быть либо явное предоставление адаптеру изолированной среды доступа к дочернему процессу, как это (из документации):

 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
 

или вы можете включить «общий» режим, изменив свой тест setup , чтобы установить Sandbox режим:

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

С последним мне повезло больше, но имейте в виду, что если вы используете какой-либо другой адаптер где-либо явно (например, для выполнения необработанных вызовов базы данных), это может привести к сбою тестов (поскольку он больше не может получить соединение).

Комментарии:

1. Хм, это странно, поскольку последнее решение, которое вы дали, на самом деле является поведением my по умолчанию ModelCase , и, похоже, оно не решает ошибку, которую я получаю. Я также обнаружил, что мой тест все еще проходит, даже если start_supervised он опущен, что означает, что каким-то образом процесс подписчика запущен. Как это может быть возможно?

2. Вы выполняете тесты async: false ?

3. Я пробовал с и без. Он имеет тот же результат

4. То, что я действительно не понимаю, — это то, как Subscriber Genserver, похоже, запускается при тестовом запуске, даже если я удаляю start_supervised вызов. Как это возможно? Это довольно стандартное приложение Phoenix, поэтому единственное другое место, где оно запускается, находится в файле приложения, где я вызываю Supervisor.start_link children

5. Похоже, что Genserver запускается тестировщиком в результате нахождения в children списке в application.ex. Я не уверен или правильный способ справиться с этим