测试时Ecto 2.0 SQL沙箱错误

时间:2016-07-12 17:38:55

标签: elixir phoenix-framework ecto

我最近将我的凤凰项目升级到了Ecto 2.0.2。我有一些代码使用Task.Supervisor.async_nolink在自己的线程上对db进行一些更新。我的测试运行时出现以下错误(仅在我的测试中发生)

[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

现在我想想我理解发生了什么:在db事务完成之前,正在检查Ecto Sandbox连接池。根据文档(至少我阅读它们的方式),解决这些问题的方法是使用共享连接池:Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})我正在做的事情。不幸的是,这不起作用。

如何设置我的测试以便不会发生此错误?

3 个答案:

答案 0 :(得分:6)

如果有其他人遇到这个问题,我会直接从语言作者Jose Valim那里得到答案:

  

是的,您对此问题的理解是正确的。这种情况正在发生,因为测试过程(拥有连接的人)已退出,但Task仍在使用其连接。使用{:shared,self()}无法修复它,因为测试仍然拥有连接,您只是隐式地共享它。

     

修复方法是在测试退出之前保证Task已完成。这可以通过调用Process.exit(task_pid,:kill)来完成。如果您不知道任务PID,可以调用Task.Supervisor.which_children(NameOfYourTaskSupervisor)来返回您随后遍历并终止它们的所有PID。但是,如果执行此方法,则测试无法同时运行(因为您可能会杀死另一个测试启动的任务)。

答案 1 :(得分:5)

我今天遇到了同样的问题,我想我已经找到了一个可能的解决方案,允许测试同时运行。

我使用此处描述的技术:http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/在运行测试时替换Task.Supervisor。

而不是:

private Product productTest;
private Category categoryTest;
private Supplier supplierTest;

@Before
public void setUp() throws Exception {
    categoryTest = new Category("Test category", "", null);
    supplierTest = new Supplier("Test supplier", null, null);
    productTest = new Product("Test product","", 10, 20, 5, supplierTest, categoryTest);

    categoryService.save(categoryTest);
    supplierService.save(supplierTest);
    productService.save(productTest);
}

@Test
public void findById() throws Exception {
    Product retrieved = productService.findById(productTest.getId());
    assertEquals(productTest, retrieved);
}

我正在做:

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

这样,我确信任务将同步运行并在测试过程之前完成。

答案 2 :(得分:0)

此问题最终由Elixir v1.8.0和db_connection v2.0.4解决。参见https://twitter.com/plataformatec/status/1091300824251285504https://elixirforum.com/t/problem-asynchronizing-ecto-calls/19796/8

如果您使用的Elixir和DBConnection版本比上述版本更新,则测试应立即可用,没有任何错误。