Вопрос

При написании кода на 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.

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

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

Как вы проводите модульное тестирование своего кода erlang?

Спасибо!

Это было полезно?

Решение

Здесь следует учитывать две вещи...

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

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

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

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

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

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

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

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

Другие советы

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

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

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

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

Инъекция

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

Швы

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

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

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

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, глубоко влюбленный в Мокито недавно перешел на Erlang, а теперь участвую в вышеупомянутом проекте.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top