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

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

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

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

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

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


person harryg    schedule 25.11.2020    source источник
comment
Вы случайно не запускаете этот тестовый модуль с async: true? Если да, то это может быть виновником.   -  person zwippie    schedule 25.11.2020
comment
Я use работаю с ModelCase модулем. Это поведение по умолчанию?   -  person harryg    schedule 25.11.2020
comment
Если у вас просто use ModelCase (а не use ModelCase, async: true), то тесты в модуле не будут выполняться одновременно с тестами в других модулях, что, вероятно, вам и нужно. (Хотя я не знаю, как выглядит ваш ModelCase)   -  person zwippie    schedule 25.11.2020
comment
Это в основном сток (теперь он называется DataCase в текущем Phoenix: github .com/phoenixframework/phoenix/blob/master/installer/).   -  person harryg    schedule 25.11.2020
comment
Я пробовал с async: false и без него, и возникает одна и та же ошибка. Я думаю, что ModelCase запускает тест неасинхронно   -  person harryg    schedule 25.11.2020


Ответы (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

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

person Everett    schedule 25.11.2020
comment
Хм, это странно, поскольку последнее решение, которое вы дали, на самом деле является поведением по умолчанию для моего ModelCase, и, похоже, оно не устраняет ошибку, которую я получаю. Я также обнаружил, что мой тест все еще проходит, даже если start_supervised опущен, что означает, что процесс подписчика каким-то образом работает. Как это может быть возможным? - person harryg; 26.11.2020
comment
Вы проводите тесты async: false ? - person Everett; 26.11.2020
comment
Я пробовал и с ним и без. Он имеет тот же результат - person harryg; 26.11.2020
comment
Я действительно не понимаю, как Subscriber Genserver запускается в тестовом прогоне, даже если я удаляю вызов start_supervised. Как это возможно? Это довольно стандартное приложение Phoenix, поэтому единственное место, где оно запускается, — это файл приложения, где я вызываю Supervisor.start_link на children - person harryg; 26.11.2020
comment
Похоже, что Genserver запускается исполнителем тестов в результате того, что он находится в списке children в application.ex. Я не уверен или правильный способ справиться с этим - person harryg; 26.11.2020
comment
Да: все в application.ex запускается во время тестового прогона — это имеет смысл, если подумать: приложение должно запуститься, если вы хотите его протестировать. - person Everett; 27.11.2020
comment
ах да, я думаю, это имеет смысл. Но это означает, что все процессы, объявленные в children, запущены и находятся под наблюдением. Для некоторых тестов я тестирую не все приложение, а только некоторые определенные функции. Вероятно, тогда ошибка в моем вопросе вызвана другим тестом. - person harryg; 27.11.2020
comment
Да, я тоже часто с этим сталкивался. Мне нужно было убедиться, что я могу переопределить параметр :name для каждого процесса, чтобы я мог вызвать другой экземпляр процесса для тестов. - person Everett; 28.11.2020