Подтвердить что ты не робот

Как сделать инъекцию зависимостей и mocks в erlang?

При написании кода на Java очень полезно включить composition и инъекция зависимостей, чтобы сделать возможным и легко выполнять чистое модульное тестирование путем издевательских взаимодействующих объектов.

Я считаю, что делать то же самое в Erlang не так просто и делает для более грязного кода.

Вероятно, это моя ошибка, поскольку я совершенно новичок в Erlang и довольно зависим от JUnit, интерфейсов EasyMock и java...

Скажем, у меня есть эта глупая функция:

%% module mymod
handle_announce(Announce) ->
    AnnounceDetails = details_db:fetch_details(Announce),
    AnnounceStats = stats_db:fetch_stats(Announce),
    {AnnounceDetails, AnnounceStats}.

При модульном тестировании mymod я хочу только доказать, что details_db и stats_db вызывается с правильными параметрами и что возвращаемые значения используются правильно. Способность details_db и stats_db генерировать правильное значение проверяется в других местах.

Чтобы решить эту проблему, я мог бы реорганизовать мой код следующим образом:

%% module mymod
handle_announce(Announce, [DetailsDb, StatsDb]) ->
    AnnounceDetails = DetailsDb:fetch_details(Announce),
    AnnounceStats = StatsDb:fetch_stats(Announce),
    {AnnounceDetails, AnnounceStats}.

И протестируйте его таким образом (в основном, приведение вызовов непосредственно в тестовый модуль):

%% module mymod_test
handle_announce_test() ->
    R = mymod:handle_announce({announce, a_value}, [?MODULE, ?MODULE, ?MODULE]),
    ?assertEqual({details,stats}, R).

fetch_details({announce, a_value}) ->
    details.

fetch_stats({announce, a_value}) ->
    stats.

Это работает, но код приложения становится грязным, и мне всегда нужно переносить этот уродливый список модулей.

Я пробовал пару макетных библиотек (erlymock и (этот другой), но я не был удовлетворен.

Как вы unit test ваш код erlang?

Спасибо!

4b9b3361

Ответ 1

Здесь есть две вещи...

Вам нужно разделить весь свой код на 2 разных типа модулей:

  • чистые функциональные модули (также свободные от побочных эффектов модули)
  • модули с побочными эффектами

(Вы должны это прочитать и убедитесь, что понимаете разницу - самый типичный побочный эффект - и тот, который находится в вашем примере кода - записывается в базу данных).

Модули, являющиеся чистым функционалом, становятся тривиальными для тестирования. Каждая экспортированная функция (по определению) всегда возвращает те же значения при вставке одних и тех же значений. Вы можете использовать рамки EUnit/Assert, которые Ричард Карлссон и Микаэль Ремонд написал. Биш- bash -bosh, работайте хорошо, не...

Главное, что около 90% вашего кода должны быть в чистых функциональных модулях - вы резко сокращаете свою проблему. (Возможно, вы думаете, что это не "решение" вашей проблемы, а просто "сокращение" ее - и вы будете в основном правы...)

Как только вы достигли этого разделения, лучший способ unit test модулей с побочными эффектами - использовать стандартную тестовую среду .

Способ, которым мы это делаем, заключается не в использовании mock-объектов, а в загрузке базы данных в функции init_per_suite или init_per_test, а затем запуск самих модулей...

Лучшим способом является переход к системным тестам как можно скорее для этого, хотя, поскольку модульные тесты являются болью для поддержания - так достаточно модульных тестов, чтобы добраться до системного теста в оба конца и не более ( даже лучше удалите тесты блока db как можно скорее).

Ответ 2

Гордон правилен тем, что основной целью является тестирование небольших побочных эффектов.

Но... хорошо, можно также протестировать интеграцию, поэтому давайте покажем, как это можно сделать.

Injection

Избегайте списков для переноса параметризованных зависимостей. Используйте запись, словарь процесса, параметризованные модули. Код будет менее уродливым.

Швы

Не сосредотачивайтесь на переменных модулях в качестве швов зависимостей, пусть процессы будут швами. Жесткое кодирование зарегистрированного имени процесса - это потерянный шанс на инъекцию зависимости.

Ответ 3

Я второй, что говорит Гатри. Вы будете удивлены, насколько ваша логика может быть извлечена в чистые функции.

Одна из вещей, которые я связывал в последнее время с новыми параметризованными модулями, - это использование параметризованных модулей для инъекции зависимостей. Это позволяет избежать проблем с списками параметров и технологическими словарями. Если вы можете использовать последние версии erlang, которые также могут быть полезными.

Ответ 4

Я просто отвечаю на вопрос, задаваемый напрямую, и не пытаюсь судить, должен ли автор делать это вообще.

Используя meck, вы можете написать unit test для примера следующим образом:

handle_announce_test() ->
    %% Given
    meck:new([details_db, stats_db]),
    meck:expect(details_db, fetch_details, ["Announce"], "AnnounceDetails"),
    meck:expect(stats_db, fetch_stats, ["Announce"], "AnnounceStats"),
    %% When
    Result = handle_announce("Announce"),
    %% Then
    ?assertMatch({"AnnounceDetails", "AnnounceStats"}, Result),
    %% Cleanup
    meck:unload().

Я использую строки только для того, чтобы подчеркнуть, что они не являются чем-то, что передается на самом деле, а скорее поддельным значением. Благодаря синтаксической подсветке их легко обнаружить в тестовом коде.

Честно говоря, я бывший разработчик Java, глубоко влюбленный в Mockito, недавно переключился на Erlang и теперь вносит свой вклад в вышеупомянутое проект.