【问题标题】:Proper testing of GenServer and Supervisor正确测试 GenServer 和 Supervisor
【发布时间】:2018-11-04 07:34:45
【问题描述】:

我对在 Elixir 中实现 Genserver 还是很陌生。我有一个案例,我正在尝试管理一些状态,而 Genserver 非常适合这种情况。但是,在测试 Genserver 时,我遇到了一些复杂情况。

我有两个测试似乎相互碰撞。我对我的 Genserver 进行了单元级测试,并进行了更高级别的测试,其中我调用的函数将 Genserver 与模块一起使用。这是我的两个测试用例:

第一:

defmodule MyApp.ScoreTableQueueTest do
  use MyApp.DataCase

  alias MyApp.{ScoreTableQueue}

  setup do
    start_supervised(ScoreTableQueue)
    :ok
  end

  test "pushes value in the queue" do
    assert :ok == ScoreTableQueue.push([1,2,3,4])
  end

  test "pops the full value of the queue" do
    assert [[1,2,3,4]] == ScoreTableQueue.pop()
  end
end

如果我单独运行它,它每次都会通过。但是如果我运行这个测试,它会定期中断:

第二:

  setup do
    start_supervised(ScoreTableQueue)
    :ok
  end

  describe "distribute" do
    test "it distrbutes the correct season points" do
      {:ok, table} = List.first(MyApp.ScoreTableAllocator.distribute())

      assert table.table_details.information == [
               %{team_id: team_3.id, team_score: "N/A"},
               %{team_id: team_2.id, team_score: ps_2.score},
               %{team_id: team_1.id, team_score: ps_1.score}
             ]

      assert table.question_id == question.id
      assert table.season_id == season.id
    end
  end

在我的distribute/1 函数中,Genserver 实际被使用了。如果我单独调用这个测试,它每次都可以正常工作。但是当我一起运行测试时,它似乎打破了一半的时间,让我相信我正在启动同一台服务器并将信息在测试之间传递到同一台服务器。

我的问题:如何将每个测试彼此分开?我希望每个测试对于每个案例都有一个完全不同的服务器,至少每个文件。 Elixir 的实现方式是什么?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    在这种情况下,我建议修改模块的 API 函数以接受服务器使用,默认为全局实例。大致如下:

    defmodule MyApp.ScoreTableQueue do
      use GenServer
    
      def push(server \\ __MODULE__, item) do
        GenServer.call(server, {:push, item})
      end
    
      ...
    end
    

    然后在您的测试中,您只需为每个测试启动一个实例:

    setup do
      {:ok, pid} = GenServer.start_link(MyApp.ScoreTableQueue, _init_args = nil)
      {:ok, queue: pid}
    end
    
    test "pushes value in the queue", %{queue: queue} do
      assert :ok == ScoreTableQueue.push(queue, [1,2,3,4])
    end
    

    当您开始使用start_supervised 时,它是根据您模块中的child_spec 函数启动的——我假设它指定了您服务器的全局实例,因此这两个测试很可能相互干扰。

    另一个选项是将测试设置为同步(通过使用use MyApp.DataCase, async: false),这样它们就不会同时运行。这可能会更简单,但如果您的测试套件很大,可能会慢一些。

    【讨论】:

    • 感谢您的回答,我只有一个问题。我试过MyApp.DataCase, async: false,但似乎没有解决问题。任何想法为什么?
    • 您没有在测试@paweł-obrok 中使用传递的queue 变量。同样作为一般反馈,我认为测试不应该相互依赖。例如,如果单独运行,“弹出队列的完整值”会失败。也许在每个测试中设置队列以对其进行操作?
    • “你没有在你的测试中使用传递的队列变量@paweł-obrok。同样作为一般反馈,我认为测试不应该相互依赖。”你是对的,固定的。
    • "作为一般反馈,我认为测试不应该相互依赖。例如,如果单独运行,“弹出队列的完整值”会失败。也许在每个中设置队列以操作所需的状态进行测试?”我当然同意。像第一部分一样为每个测试创建一个新的 GenServer 时,设置完整状态要容易得多。我怀疑正在发生的事情,即使async: false 是一些测试使服务器处于非干净状态。我没用过start_supervised,所以不能确定。
    猜你喜欢
    • 2017-04-22
    • 2017-02-07
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 2016-06-23
    • 2015-03-29
    • 2019-12-05
    • 2019-09-04
    相关资源
    最近更新 更多