Как сделать инъекцию зависимостей и макеты в эрланге? - PullRequest
21 голосов
/ 29 июля 2009

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

Я считаю, что делать то же самое в 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 и ( эта другая ), но я не был удовлетворен.

Как вы тестируете свой код erlang?

Спасибо!

Ответы [ 4 ]

22 голосов
/ 30 июля 2009

Здесь нужно учитывать две вещи ...

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

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

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

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

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

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

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

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

4 голосов
/ 06 августа 2009

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

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

4 голосов
/ 31 июля 2009

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

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

Инъекции

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

пласты

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

3 голосов
/ 26 октября 2012

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

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

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 и теперь участвующий в вышеупомянутом проекте.

...