测试中的 Ecto 2.0 SQL 沙盒错误

Posted

技术标签:

【中文标题】测试中的 Ecto 2.0 SQL 沙盒错误【英文标题】:Ecto 2.0 SQL Sandbox Error on tests 【发布时间】:2016-11-15 02:15:46 【问题描述】:

我最近将我的 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 连接池。根据文档(至少我阅读它们的方式),解决这些问题的方法是使用共享连接池:Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :shared, self()),我正在这样做。不幸的是,这不起作用。

如何设置我的测试,以免发生此错误?

【问题讨论】:

【参考方案1】:

如果其他人遇到这种情况,我会直接从语言作者 Jose Valim 那里得到答复:

是的,您对问题的理解是正确的。这是因为拥有连接的测试进程已经退出,但任务仍在使用它的连接。使用 :shared, self() 并不能修复它,因为测试仍然拥有连接,您只是在隐式共享它。

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

【讨论】:

TLDR:没有解决办法。 感谢您报告发现的内容。我使用 Jose 的反馈为我很好地解决了这个问题。所以,这就是解决方案。【参考方案2】:

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

我正在使用此处描述的技术: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/test.exs 中添加config :app, :task_supervisor, TestTaskSupervisor

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

【讨论】:

【参考方案3】:

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 版本比上面提到的更新,测试应该可以直接运行,没有任何错误。

【讨论】:

使用 elixir 1.10.3 和 db_connection 2.2.2。仍然面临这个问题。

以上是关于测试中的 Ecto 2.0 SQL 沙盒错误的主要内容,如果未能解决你的问题,请参考以下文章

文件 lib/phoenix_ecto/html.ex 上的编译错误

贝宝沙盒测试错误

iOS PayPal 沙盒模式下的负错误测试

Ecto 子查询中的 SQL WITH AS 语句

设置沙盒测试员帐户时出现“无效年份”错误

在 Elixir 中将 Ecto 模型编码为 JSON