2017-07-16 21 views
0

我有一個菲尼克斯測試應用程序與產品模式。 我有一個由主應用程序管理器啓動的GenServer,它獲取帶有handle_call的產品列表。Ecto沙盒使用檢出連接現有過程

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(產品)的測試找到他們,但是運行MyGenServer.get_products()將返回一個空陣列。

我是沒有得到任何錯誤,但只是返回一個空數組,就好像沒有產品存在一樣。

有什麼方法可以讓現有的PID使用結帳沙箱連接,並在GenServer進程中檢索我的產品?

PS。我設法通過在測試設置中重新啓動GenServer進程來運行測試,但我想知道是否有更「優雅」的方式來解決這個問題。

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

感謝

+0

什麼是調用'GenServer'做這項工作的目的是什麼?爲什麼不只是調用一個函數,這樣你就不會阻止對'GenServer'的每個請求。 –

+0

嗨,對不起,我不是很清楚。這只是一個測試例子。我把'products = Repo.all(Product)'放在一個簡單的與數據庫的交互中。 因此,這是關於Ecto Sandbox行爲的問題,而不是關於GenServer結果的問題 – iacobSon

+0

在'test_helper.exs'中調用了'Ecto.Adapters.SQL.Sandbox.mode(Repo,:manual)'嗎? 您是使用phoenix生成器的'ConnCase'或'DataCase'模板嗎? 你用'async:true'運行測試嗎?如果沒有,那麼它應該在':shared'模式下自動工作。 'test_helper的 –

回答

0

下面是工作與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) 
+0

謝謝邁克!這與我的例子非常接近,而我的這種方式仍然不起作用。我現在可以發現的唯一區別是,我使用ExMachina在測試中生成數據,並且我在GenServer的'init'函數中運行了一些與Repo相關的查詢。 今天晚上我會嘗試刪除ExMachina,看看它是否有任何區別。 你有上面的例子Github回購?我也可以試着去研究它。 – iacobSon

+0

啊,在GenServer init回調中訪問Repo是個問題。在test_helper可以將其切換到手動模式之前,Sandbox會將一個連接與一個打開的事務與GenServer進程相關聯。 –

+0

在'init'結尾顯式調用'Ecto.Adapters.SQL.Sandbox.checkin(Repo)'來修復測試,但我不確定這是否是處理它的最好方法。 –