Ecto Sandbox 对现有进程使用检出连接

Ecto Sandbox use checked out connection for existing process

我有一个带有产品架构的 Phoenix Test 应用程序。 我有一个由主应用程序主管启动的 GenServer,它获取带有 handle_call.

的产品列表
def handle_call(:get_products, _from, _state)
  products = Repo.all(Product)
  {:reply, products, products}
end

现在我想为这个GenServer写一个测试。

我试过在测试中做这样的事情

setup do
  pid = Process.whereis(MyGenServer)
  Ecto.Adapters.SQL.Sandbox.allow(Repo, self(), pid)
  ProductFactory.insert_list(3, :product) # using ExMachina for factories
end

创建了 3 个产品,我可以在 Repo.all(Product) 的测试中找到它们,但是 运行 宁 MyGenServer.get_products() 将 return 一个空数组.

没有收到任何错误,只是return一个空数组,好像不存在任何产品。

有什么方法可以让现有的 PID 使用结帐沙箱连接,并在 GenServer 进程中检索我的产品?

PS。我通过在测试设置中重新启动 GenServer 进程设法 运行 测试,但我想知道是否有更多 "elegant" 方法来解决问题。

setup do
  Supervisor.terminate_child(MyApp.Supervisor, MyGenServer)
  Supervisor.restart_child(MyApp.Supervisor, MyGenServer)
  ProductFactory.insert_list(3, :product)
end

谢谢

这是一个最小的 phoenix 应用程序,它与在应用程序主管中启动的 GenServer 一起工作,使用 :shared 模式进行数据库交互。

应用模块:

defmodule TestGenServers.Application do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec

    children = [
      supervisor(TestGenServers.Repo, []),
      supervisor(TestGenServers.Web.Endpoint, []),
      worker(TestGenServers.MyServer, [])
    ]

    opts = [strategy: :one_for_one, name: TestGenServers.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

产品模块:

defmodule TestGenServers.Model.Product do
  use Ecto.Schema
  import Ecto.Changeset
  alias TestGenServers.Model.Product


  schema "model_products" do
    field :name, :string

    timestamps()
  end

  @doc false
  def changeset(%Product{} = product, attrs) do
    product
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end

GenServer 模块:

defmodule TestGenServers.MyServer do
  use GenServer
  alias TestGenServers.Repo

  def start_link() do
    GenServer.start_link(__MODULE__, nil, name: __MODULE__)
  end

  def handle_call({:get_product, id}, _caller, state) do
    {:reply, TestGenServers.Repo.get(TestGenServers.Model.Product, id), state}
  end

end

测试模块:

defmodule TestGenServers.TestMyServer do
  use TestGenServers.DataCase

  setup do
    product = Repo.insert!(%TestGenServers.Model.Product{name: "widget123"})
    %{product_id: product.id}
  end

  test "talk to gen server", %{product_id: id} do
    assert %{id: ^id, name: "widget123"} = GenServer.call(TestGenServers.MyServer, {:get_product, id})
  end
end

DataCase 模块

defmodule TestGenServers.DataCase do
  use ExUnit.CaseTemplate

  using do
    quote do
      alias TestGenServers.Repo
      import TestGenServers.DataCase
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(TestGenServers.Repo)
    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(TestGenServers.Repo, {:shared, self()})
    end
    :ok
  end
end

test_helper:

ExUnit.start()

Ecto.Adapters.SQL.Sandbox.mode(TestGenServers.Repo, :manual)