Search code examples
erlangelixirerlang-otpgen-serverex-unit

In Elixir ExUnit, how do I guarantee that the Supervisor will create a new GeNserver?


I'm learning to test in Elixir and this problem appeared:

When I run the following test, sometimes it passes and sometimes don't, I'm thinking it is the fact that the Supervisor don't have the time to restart the GenServer:

  test "Supervisor will revive the dead gensever" do
    {:ok, pid} = KV.Supervisor.start_link([])
    KV.RegistryClient.create KV.RegistryClient, "wallet"
    [h | _] = Supervisor.which_children(pid)
    {KV.RegistryClient, pid_reg, _, _} = h
    send(pid_reg, :insta_kill)

    assert %{active: 1} = Supervisor.count_children(pid)
  end

When occurs, this is the error:

1) test Supervisor will revive the dead gensever (KV.RegistryTest)
     test/kv/registry_test.exs:35
     match (=) failed
     code:  assert %{active: 1} = Supervisor.count_children(pid)
     right: %{active: 0, specs: 1, supervisors: 0, workers: 1}
     stacktrace:
       test/kv/registry_test.exs:41: (test)

How do I prevent this to happen? A timeout is a good approach?


Solution

  • The only way you can effectively test this behaviour without race conditions is by:

    1. Making sure the old process is dead. This can be done by monitoring the process before you send the kill signal and then assert_receive {:DOWN, ^monitor_ref, _, _, _}

    2. Query the supervisor until the active count changes to one. This can be done by executing a function once every 10ms or so

    However, as others have said, this behaviour is guaranteed by Erlang/OTP. Therefore, instead of testing if a supervisor is actually restarting something, I would rather test if you are passing the proper child specification to the supervisor. So assuming the supervisor starts a processed based on KV.Registered, I would do this:

    assert %{restart: :permanent} = Supervisor.child_spec(KV.Registered)
    

    In other words, I would test the contract with supervisors, without testing the supervisors themselves.