2015-07-30 4 views
5

Я искал битку и немного не мог найти что-либо по этому вопросу - либо эликсир слишком молод, либо я искал неправильные термины.Лучшая практика для тестирования супервизоров в Elixir

Я работаю над учебником по проекту Elixir Portal от Jose Valim (https://howistart.org/posts/elixir/1) и собираю тесты для практики (я создал всю функциональность).

Часть учебного пособия предназначена для создания супервизора, чтобы сделать Portal.Door модулем отказоустойчивым.

Я пытаюсь проверить отказоустойчивость (например, что руководитель перезапускает экземпляр Portal.Door, если он неправильно выключение), используя следующий код

defmodule PortalTest do 
    use ExUnit.Case, async: true 

    ... 

    test "supervisor restarts doors" do 
    {:ok, pid} = Portal.shoot(:third) 
    Process.unlink(pid) 
    Process.exit(pid, :shutdown) 
    assert Portal.Door.get(:third) == [] #new doors initialize with an empty array 
    end 

end 

Но я получаю эту ошибку, когда я бегу тест:

1) test supervisor restarts doors (PortalTest) 
    test/portal_test.exs:35 
    ** (exit) exited in: GenServer.call(:third, {:get, #Function<3.47016826/1 in Portal.Door.get/1>}, 5000) 
     ** (EXIT) shutdown 
    stacktrace: 
     (elixir) lib/gen_server.ex:356: GenServer.call/3 
     test/portal_test.exs:39 

Итак, мне интересно, есть ли лучший способ сделать это, или мой код просто плохой.

ответ

1

Process.exit/1 посылает сигнал выхода, но не ждет завершения процесса. Судя по выходу вашей ошибки, он выглядит как Portal.Door.get/1, а затем сбой, так как процесс gen_server завершается, прежде чем он получит сообщение о вызове.

Чтобы преодолеть это, вам нужно дождаться завершения процесса и перезапустить его снова. Простым средством может быть короткий сон (скажем, 100 мс) через :timer.sleep/1 после того, как вы выдадите сигнал выхода.

Более привлекательный подход заключается в том, чтобы дождаться завершения процесса, а затем снова перезапустить. Первую часть можно легко сделать, настроив монитор (через Process.monitor/1) и дождитесь соответствующего сообщения :DOWN. Делая это, вы также убедитесь, что целевой процесс действительно прекратился.

Затем вам нужно дождаться повторного запуска процесса, чтобы вы могли отправить запрос. Это может быть сложно, и спать в течение короткого времени, вероятно, самый простой вариант. В качестве альтернативы, если процесс зарегистрирован под локальным псевдонимом, вы можете опросить Process.whereis/1, пока не получите значение, отличное от нуля, после чего вы узнаете, что процесс снова запущен.

+0

На самом деле я возвращаю свое первоначальное заявление. Похоже, что когда я использую IO.inspect для выполнения некоторой отладки, это вызывает задержку, которая достаточно долго для перезапуска процесса. Без него использование recursion для ожидания Process.whereis/1 для возврата чего-то другого, кроме значения nil, похоже, не работает (вероятно, моя ошибка). Итак, я собираюсь: timer.sleep/1 на данный момент. – user2577226

0

Вот пример рабочего кода, основанный в основном на советах @sasajuric.

defmodule Namer.Worker.Test do 
    use ExUnit.Case 

    test "supervisor restarts worker on server crash" do 
    pid = Process.whereis(Namer.Worker) 
    ref = Process.monitor(pid) 
    Process.exit(pid, :kill) 
    receive do 
     {:DOWN, ^ref, :process, ^pid, :killed} -> 
     :timer.sleep 1 
     assert is_pid(Process.whereis(Namer.Worker)) 
    after 
     1000 -> 
     raise :timeout 
    end 
    end 
end 
Смежные вопросы