#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. Я не уверен или правильный способ справиться с этим