Ecto 2.0 SQL Sandbox Ошибка на тестах

Я недавно обновил свой проект Phoenix до Ecto 2.0.2. У меня есть код, который использует Task.Supervisor.async_nolink сделать некоторые обновления БД в своем собственном потоке. При выполнении моих тестов я получаю следующую ошибку (происходит только на моих тестах)

[error] Postgrex.Protocol (#PID<0.XXX.0>) disconnected: **
(DBConnection.ConnectionError) owner #PID<0.XXX.0> exited while 
client #PID<0.XXX.0> is still running with: shutdown

Теперь я думаю, что понимаю, что происходит: пул соединений Ecto Sandbox проверяется до завершения транзакции db. Согласно документам (по крайней мере, так, как я их читаю), способ обойти это - использовать общий пул соединений: Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()}) что я делаю. К сожалению, это не работает.

Как настроить тесты, чтобы эта ошибка не возникала?

2 ответа

Решение

Если кто-то еще сталкивался с этим, я получил ответ на этот вопрос непосредственно от автора языка Хосе Валима:

Да, вы понимаете проблему правильно. Это происходит потому, что тестовый процесс, тот, кто владеет соединением, завершился, но Задача все еще использует свое соединение. Использование {:shared, self()} не исправляет это, потому что тест все еще владеет соединением, вы просто делитесь им неявно.

Способ устранения - гарантировать, что Задание завершено до завершения теста. Это можно сделать, вызвав Process.exit(task_pid,:kill). Если вы не знаете PID задачи, вы можете вызвать Task.Supervisor.which_children(NameOfYourTaskSupervisor), чтобы вернуть все PID, которые вы затем пройдете, и убить их. Однако, если вы используете этот подход, тест не может выполняться одновременно (так как вы можете убить задачи, запущенные другим тестом).

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

Я использую методику, описанную здесь: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ чтобы заменить Task.Supervisor во время выполнения тестов.

Вместо:

Task.Supervisor.async_nolink(TaskSupervisor, fn -> (...) end)

Я делаю:

@task_supervisor Application.get_env(:app, :task_supervisor) || Task.Supervisor
# ...
@task_supervisor.async_nolink(TaskSupervisor, fn -> (...) end)

а потом я определяю TestTaskSupervisor

defmodule TestTaskSupervisor do
  def async_nolink(_, fun), do: fun.()
end

и добавить config :app, :task_supervisor, TestTaskSupervisor в config/test.exs,

Таким образом, я уверен, что задача будет выполняться синхронно и завершится до начала процесса тестирования.

Эта проблема, наконец, решена с помощью Elixir v1.8.0 и db_connection v2.0.4. См. https://twitter.com/plataformatec/status/1091300824251285504 и https://elixirforum.com/t/problem-asynchronizing-ecto-calls/19796/8

Если вы используете версии Elixir и DBConnection, более новые, чем упомянутые выше, тест должен работать без каких-либо ошибок.

Другие вопросы по тегам