Как в Elixir ExUnit гарантировать, что Supervisor создаст новый GeNserver?

Учусь тестить в Эликсире и вот такая проблема появилась:

Когда я запускаю следующий тест, иногда он проходит, а иногда нет, я думаю, что это связано с тем, что у супервизора нет времени перезапустить 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

Когда происходит, это ошибка:

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)

Как предотвратить это? Тайм-аут - хороший подход?


person Mateus Luiz    schedule 22.05.2019    source источник
comment
Вероятно, вам лучше всего добавить короткий сон к вашему тесту между убийством и утверждением.   -  person Justin Wood    schedule 22.05.2019
comment
Почему вы хотите протестировать OTP? Вы не доверяете 40-летнему проверенному решению? Вы должны тестировать свой код, а не код OTP. Последний работает, примите это как должное. ExUnit не предоставляет удобного способа протестировать его именно потому, что никто не должен тестировать внутренности OTP.   -  person Aleksei Matiushkin    schedule 23.05.2019
comment
@AlekseiMatiushkin Я просто пытаюсь изучить, как работает OTP, используя тесты, такие как TDD   -  person Mateus Luiz    schedule 24.05.2019


Ответы (2)


Единственный способ эффективно протестировать это поведение без условий гонки:

  1. Убедитесь, что старый процесс мертв. Это можно сделать, наблюдая за процессом перед отправкой сигнала уничтожения, а затем assert_receive {:DOWN, ^monitor_ref, _, _, _}

  2. Запрашивайте супервизора до тех пор, пока количество активных не изменится на единицу. Это можно сделать, выполняя функцию один раз каждые 10 мс или около того.

Однако, как уже говорили другие, такое поведение гарантируется Erlang/OTP. Поэтому вместо того, чтобы проверять, действительно ли супервизор что-то перезапускает, я бы предпочел проверить, передаете ли вы правильную дочернюю спецификацию супервизору. Итак, предполагая, что супервизор запускает обработку на основе KV.Registered, я бы сделал следующее:

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

Другими словами, я бы протестировал контракт с супервайзерами, не проверяя самих супервайзеров.

person José Valim    schedule 23.05.2019
comment
Спасибо за отзыв, я попробую это - person Mateus Luiz; 24.05.2019

Это вопрос времени. Короткая версия: не утруждайте себя тестированием OTP. IT уже очень хорошо протестирован.

Но если в будущем вам понадобится обеспечить правильную работу стартапов, чтобы понять, как супервизоры запускают серверы, посмотрите это видео https://www.youtube.com/watch?v=2i_XrY5CCXE

person Zachary K    schedule 23.05.2019