Ecto 2.0 SQL Sandbox Error Testing

I recently upgraded my phoenix project to Ecto 2.0.2. I have some code that uses Task.Supervisor.async_nolink to do some updates for db in its thread. I get the following error while running my tests (only in my tests)

 [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 

Now I think I understand what is happening: the Ecto Sandbox connection pool is checked back before the db transaction is completed. According to the docs (at least the way I read them), the way around this is to use a common connection pool: Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()}) , which I I do. Unfortunately this does not work.

How to configure my tests so that this error does not occur?

+6
source share
2 answers

If anyone else encounters this, I received an answer to this directly from the author of the language, Jose Valima:

Yes, your understanding of the problem is correct. This is because the test process, the one who owns the connection, exited, but Task still uses its connection. Using {: shared, self ()} does not fix it, because the test still owns the connection, you just share it implicitly.

The fix is ​​to ensure that the Task completes before the test completes. This can be done by calling Process.exit (task_pid ,: kill). If you do not know the task PID, you can call Task.Supervisor.which_children (NameOfYourTaskSupervisor) to return all the PIDs that you then go through and kill. However, if you take this approach, the test cannot be executed at the same time (since you can kill tasks launched by another test).

+3
source

I had the same problem today, and I think I have found a possible solution to run the tests at the same time.

I use the technique described here: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ to replace Task.Supervisor during test execution.

Instead:

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

I do:

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

and then I define TestTaskSupervisor

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

and add config :app, :task_supervisor, TestTaskSupervisor to config/test.exs .

Thus, I am sure that the task will be executed synchronously and complete before the testing process.

+3
source

All Articles