代码之家  ›  专栏  ›  技术社区  ›  Mr H

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

  •  2
  • Mr H  · 技术社区  · 6 年前

    我找到了一些关于如何测试生产者的资源,但是我找不到任何东西可以说明如何测试消费者。

    在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
    

    2 回复  |  直到 6 年前
        1
  •  0
  •   bschaeffer    6 年前

    没有理由使用 ex_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
    

    那你就可以 TestProducer :

    defmodule TestProducer
      use GenStage
    
      def notify(pid, event) do
        GenServer.cast(pid, {:notify, event})
      end
    
      def start_link do
        GenStage.start_link(__MODULE__, :ok)
      end
    
      def init(:ok) do
        {:producer, :ok, dispatcher: GenStage.BroadcastDispatcher}
      end
    
      def handle_demand(_demand, state) do
        {:noreply, [], state}
      end
    
      def handle_cast({:notify, event}, state) do
        {:noreply, [event], state}
      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; 如果您在代码库中与许多不同的使用者一起工作,那么必须使用手动/测试事件生成器。消费者并不真正关心生产者如何制作事件,只关心它可以订阅和消费事件。因此,您的测试只需要确保消费者能够接收来自任何生产者的事件,并且您可以向他们发送它在测试中寻找的正确事件。

        2
  •  0
  •   Mr H    6 年前

    在对消费者的测试中:

     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
    

    我想,

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