Elixir 如何测试长生不老药GenStage消费者?

Elixir 如何测试长生不老药GenStage消费者?,elixir,producer-consumer,consumer,ex-unit,genstage,Elixir,Producer Consumer,Consumer,Ex Unit,Genstage,我找到了一些关于如何测试生产者的资源,但是我找不到任何可以显示如何测试消费者的资源 在producer中,我创建了一个虚拟消费者,一切正常,但在consumer中,我正在努力进行测试 defmodule DataProducer do use GenStage def start_link([]) do GenStage.start_link(__MODULE__, 0, name: __MODULE__) end # {:qu

我找到了一些关于如何测试生产者的资源,但是我找不到任何可以显示如何测试消费者的资源

在producer中,我创建了一个虚拟消费者,一切正常,但在consumer中,我正在努力进行测试

defmodule DataProducer do
      use GenStage

      def start_link([]) do
        GenStage.start_link(__MODULE__, 0, name: __MODULE__)
      end

      # {:queue.new, demand, size}
      def init(counter) do
        {:producer, counter, dispatcher: GenStage.BroadcastDispatcher}
      end

      def handle_demand(demand, state) do 
        events = Enum.to_list(state..state + demand + 1)
        # Logger.info "demand is: #{inspect(demand)}, state is #{inspect(state)}"
        {:noreply, events, (state + demand)}
      end
    end
生产商测试:

 defmodule DataProducerTest do
      use ExUnit.Case

      test "check the results" do
        {:ok, stage} = DataProducer.start_link([])
        {:ok, _cons} = TestConsumer.start_link(stage)
        assert_receive {:received, events}
        GenStage.stop(stage)
      end

    end

    defmodule TestConsumer do
      def start_link(producer) do
        GenStage.start_link(__MODULE__, {producer, self()})
      end
      def init({producer, owner}) do
        {:consumer, owner, subscribe_to: [producer]}
      end
      def handle_events(events, _from, owner) do
        send(owner, {:received, events})
        {:noreply, [], owner}
      end
    end
和消费者:

defmodule DataConsumer do
  use GenStage
  def start_link([]) do
    GenStage.start_link(__MODULE__, :any_state)
  end
  def init(state) do
    {:consumer, state, subscribe_to: [{DataProducer, selector: fn n -> n > 50 && n < 100 end, max_demand: 10}]}
  end
  def handle_events(events, _from, state) do
    for event <- events do
      # :timer.sleep(250)
      Logger.info inspect( {self(), event, state} )
    end
    {:noreply, [], state}
  end
end
 test "should behave like consumer" do
    {:ok, producer} = DummyProducer.start_link(1)
    {:ok, consumer} = Consumer.start_link(producer)
    Process.register self, :test
    assert_receive {:called_back, 10}
  end
defmodule-DataConsumer-do
使用发电机组
def启动链接([])do
GenStage.start\u链接(\u模块\u,:任何\u状态)
结束
def初始(状态)do
{:消费者,州,订阅:[{DataProducer,选择器:fn n->n>50&&n<100结束,最大需求:10}]}
结束
def处理事件(事件、来自、状态)do

对于事件没有理由在此处使用
ex\u mock
。如果你让制作人和你的消费者同意这样一个论点,那就容易多了:

defmodule DataConsumer do
  use GenStage

  def start_link(producer) do
    GenStage.start_link(__MODULE__, producer)
  end

  def init(producer) do
    {:consumer, state, subscribe_to: [{producer, selector: fn n -> n > 50 && n < 100 end, max_demand: 10}]}
  end
end
并在测试中订阅它,并对预期结果进行断言:

defmodule DataConsumerTest do
  use ExUnit.Case

  test "consumes events" do
    {:ok, pid} = TestProducer.start_link()
    DataConsumer.start_link(pid)
    TestProducer.notify(%{data: :event_data})

    # assert thing you expected to happen happens
  end
end

TLDR如果您在代码库中与许多不同的使用者合作,则必须有手动/测试事件生成器。消费者并不真正关心生产者如何制作事件,只关心它可以订阅和消费事件。因此,您的测试只需确保消费者能够从任何生产者接收事件,并且您可以向他们发送其在测试中查找的正确事件。

在消费者测试中:

defmodule DataConsumer do
  use GenStage
  def start_link([]) do
    GenStage.start_link(__MODULE__, :any_state)
  end
  def init(state) do
    {:consumer, state, subscribe_to: [{DataProducer, selector: fn n -> n > 50 && n < 100 end, max_demand: 10}]}
  end
  def handle_events(events, _from, state) do
    for event <- events do
      # :timer.sleep(250)
      Logger.info inspect( {self(), event, state} )
    end
    {:noreply, [], state}
  end
end
 test "should behave like consumer" do
    {:ok, producer} = DummyProducer.start_link(1)
    {:ok, consumer} = Consumer.start_link(producer)
    Process.register self, :test
    assert_receive {:called_back, 10}
  end
现在
DummyProducer

defmodule DummyProducer do
  use GenStage

  def start_link(demand) do
    GenStage.start_link(__MODULE__, demand)
  end

  def init(demand) do
    {:producer, demand}
  end

  def handle_demand(demand, counter) when demand > 0 do
    events = Enum.to_list(counter..counter+demand-1)
    Process.send_after(self(), {:stop, demand}, 1)
    {:noreply, events, demand + counter}
  end

  def handle_info({:stop, demand}, state) do
    send :test, {:called_back, demand}
    {:stop, :normal, demand}
  end
end
我想,


测试消费者的重点是检查消费者是否可以发送需求并坚持订阅中分配的最大需求。

您可以使用ex_mock模拟DataProducer,或者使用自定义测试生产者参数化producer module来初始化消费者。感谢您的回复。你能举个例子说明你期望发生的事情吗?感谢您的实施,如何才能满足消费者的需求?对不起,我看不出这个测试是如何工作的。您提到了
,所以您的测试只需要确保消费者能够接收事件
当消费者在状态中不返回事件时,您如何测试,因此应该测试生产者是否接收到新的需求。这是正确的吗?@MrH例如,我会测试是否创建了记录,或者是否执行了我的消费者正在执行的任何操作。此外,对于测试,我们忽略消费者请求的需求,而只是手动生成事件
handle\u cast({:notify,event},state)
将始终将这些事件发送给您的消费者。太长,读不下去了需求循环就在那里,这样当消费者可以自由接受新事件时,您就可以响应需求。如果您不关心需求(在这个测试用例中我们不关心),您可以忽略它,并随时向消费者发送事件。