今時のErlangプロジェクトの作り方(3/3)

# rebarでテスト

テストを作って実行してみます。
まずはフォルダを作ります。

```shell-session
mkdir -p test
```

eunitによるテストを追加します。

```erlang:test/test_myapp_hello.erl
-module(test_myapp_hello).
-include_lib("eunit/include/eunit.hrl").
-define(MOD, myapp_hello).

setup() ->
     {ok, Started} = application:ensure_all_started(myapp),
     Started.

cleanup(Started) ->
    [application:stop(App) || App <- Started].

all_test_() ->
    {setup,
     fun setup/0,
     fun cleanup/1,
     [
        fun test1/0,
        fun test2/0
     ]
    }.

test1() ->
    ?assertEqual(ok, gen_server:call(?MOD, hi)),
    ?assert(is_alive_process()).


test2() ->
    ?assert(is_alive_process()),
    ?assertEqual(ok, gen_server:call(?MOD, stop)),
    ?assert(not is_alive_process()).

is_alive_process() ->
    Alives = lists:filter(fun(X) -> X =:= ?MOD end, registered()),
    length(Alives) =:= 1.
```

テストを実行します。

```shell-session
% rebar3 eunit

===> Verifying dependencies...
===> Compiling myapp
===> Performing EUnit tests...
- start_link() 
- init([]) 
hello world- handle_call(hi, {<0.103.0>,#Ref<0.0.1.233>}, {}) 
- handle_call(stop, {<0.103.0>,#Ref<0.0.1.236>}, {}) 
.normal terminate {}
.

Top 2 slowest tests (0.001 seconds, 1.8% of total time):
  test_myapp_hello:all_test_/0
    0.001 seconds
  test_myapp_hello:all_test_/0
    0.000 seconds

Finished in 0.056 seconds
2 tests, 0 failures

=INFO REPORT==== 5-Dec-2016::15:13:02 ===
    application: myapp
    exited: stopped
    type: temporary
```

テストが通りました！

## rebarで依存関係解決

ログフレームワークのlagerを例に依存関係の解決をしてみます。
起動時に立ち上げるプロセスとしてlagerを追加します。

```erlang:apps/myapp/src/myapp.app.src
{application, myapp,
 [{description, "An OTP application"},
  {vsn, "0.1.0"},
  {registered, []},
  {mod, { myapp_app, []}},
  {applications,
   [kernel,
    stdlib,
    lager
   ]},
  {env,[]},
  {modules, []},

  {maintainers, []},
  {licenses, []},
  {links, []}
 ]}.
```

io:formatからlager:debugに変更します。

```erlang:apps/myapp/src/myapp_hello.erl
-module(myapp_hello).

-behavior(gen_server).

-define(DELAY, 1000).

-export([
         start_link/0,
         stop/0,
         init/1,
         handle_call/3,
         handle_cast/2,
         handle_info/2,
         code_change/3,
         terminate/2
        ]).

start_link() ->
    lager:debug("- start_link() ~n"),
    gen_server:start_link({local, myapp_hello}, ?MODULE, [], []).

stop() ->
    lager:debug("- stop() ~n"),
    gen_server:call(?MODULE, stop).

init(Args) ->
    lager:debug("- init(~p) ~n", [Args]),
    lager:debug("hello world"),
    {ok, {}}.

handle_call(stop, From, State) ->
    lager:debug("- handle_call(stop, ~p, ~p) ~n", [From, State]),
    {stop, normal, ok, State};
handle_call(Message, From, State) ->
    lager:debug("- handle_call(~p, ~p, ~p) ~n", [Message, From, State]),
    {reply, ok, State, ?DELAY}.

handle_cast(Message, State) ->
    lager:debug("- handle_cast(~p, ~p) ~n", [Message, State]),
    {noreply, State, ?DELAY}.

handle_info(timeout, State) ->
    lager:debug("- handle_info(timeout, ~p) ~n", [State]),
    {noreply, State, ?DELAY};
handle_info(Message, State) ->
    lager:debug("- handle_info(~p, ~p) ~n", [Message, State]),
    {noreply, State, ?DELAY}.

code_change(OldVsn, State, Extra) ->
    lager:debug("- code_change(~p, ~p, ~p) ~n", [OldVsn, State, Extra]),
    {ok, State}.

terminate(Reason, State) ->
    lager:debug("~p terminate ~p~n", [Reason, State]).
```

rebar.configのdepsにlagerを追加します。
またlagerはコンパイルオプション{parse_transform, lager_transform}が必要なのでそれも追加します。

```erlang:rebar.config
{erl_opts, [
    debug_info,
    {parse_transform, lager_transform}
]}.

{deps, [
    {lager, {git, "git://github.com/basho/lager", {tag, "3.2.4"}}}
]}.

{relx, [{release, { myapp, "0.1.0" },
         [
          lager,
          myapp,
          sasl
         ]},

        {sys_config, "./config/sys.config"},
        {vm_args, "./config/vm.args"},

        {dev_mode, true},
        {include_erts, false},

        {extended_start_script, true}]
}.

{profiles, [{prod, [{relx, [{dev_mode, false},
                            {include_erts, true}]}]
            }]
}.
```

この状態でビルドしてみます。
初回ビルド時にgitリポジトリからlagerがダウンロードされ、さらにlagerが依存するgoldrushもダウンロードされています。

```shell-session
% rebar3 release
===> Verifying dependencies...
===> Fetching lager ({git,"git://github.com/basho/lager",
                                 {ref,"81eaef0ce98fdbf64ab95665e3bc2ec4b24c7dac"}})
===> Fetching goldrush ({git,"https://github.com/basho/goldrush.git",
                                   {ref,
                                       "8f1b715d36b650ec1e1f5612c00e28af6ab0de82"}})
===> Compiling goldrush
===> Compiling lager
===> Compiling myapp
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
          /home/tadokoro/git/erlang_start/myapp/_build/default/lib
          /home/tadokoro/git/erlang_start/myapp/apps
          /usr/lib/erlang/lib
===> Resolved myapp-0.1.0
===> Dev mode enabled, release will be symlinked
===> release successfully created!
```

最後に実行してみます。
出力がio:formatからlagerに置き換わっています。

```shell-session
% ./_build/default/rel/myapp/bin/myapp foreground
Exec: ~bin/erlang/19.1/erts-8.1/bin/erlexec -noshell -noinput +Bd -boot ~/myapp/_build/default/rel/myapp/releases/0.1.0/myapp -mode embedded -config ~/myapp/_build/default/rel/myapp/releases/0.1.0/sys.config -boot_var ERTS_LIB_DIR ~bin/erlang/19.1/erts-8.1/../lib -args_file ~/myapp/_build/default/rel/myapp/releases/0.1.0/vm.args -- foreground
Root: ~/myapp/_build/default/rel/myapp
10:24:44.886 [debug] Lager installed handler {lager_file_backend,"error.log"} into lager_event
10:24:44.886 [debug] Lager installed handler {lager_file_backend,"console.log"} into lager_event
10:24:44.886 [debug] Lager installed handler error_logger_lager_h into error_logger
10:24:44.887 [debug] Supervisor gr_param_sup started gr_param:start_link(gr_lager_default_tracer_params) at pid <0.332.0>
10:24:44.887 [debug] Supervisor gr_counter_sup started gr_counter:start_link(gr_lager_default_tracer_counters) at pid <0.333.0>
10:24:44.887 [debug] Supervisor gr_manager_sup started gr_manager:start_link(gr_lager_default_tracer_params_mgr, gr_lager_default_tracer_params, []) at pid <0.334.0>
10:24:44.887 [debug] Supervisor gr_manager_sup started gr_manager:start_link(gr_lager_default_tracer_counters_mgr, gr_lager_default_tracer_counters, [{input,0},{filter,0},{output,0},{job_input,0},{job_run,0},{job_time,0},{job_error,0}]) at pid <0.335.0>
10:24:44.892 [info] Application lager started on node myapp@tvb
10:24:44.892 [debug] - start_link() ~n
10:24:44.892 [debug] - init([]) 
10:24:44.892 [debug] hello world
10:24:44.892 [debug] Supervisor myapp_sup started myapp_hello:start_link() at pid <0.341.0>
10:24:44.893 [info] Application myapp started on node myapp@tvb

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
          supervisor: {local,sasl_safe_sup}
             started: [{pid,<0.347.0>},
                       {id,alarm_handler},
                       {mfargs,{alarm_handler,start_link,[]}},
                       {restart_type,permanent},
                       {shutdown,2000},
                       {child_type,worker}]
10:24:44.893 [debug] Supervisor sasl_safe_sup started alarm_handler:start_link() at pid <0.347.0>

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
          supervisor: {local,sasl_sup}
             started: [{pid,<0.346.0>},
                       {id,sasl_safe_sup},
                       {mfargs,
                           {supervisor,start_link,
                               [{local,sasl_safe_sup},sasl,safe]}},
                       {restart_type,permanent},
                       {shutdown,infinity},
                       {child_type,supervisor}]
10:24:44.893 [debug] Supervisor sasl_sup started supervisor:start_link({local,sasl_safe_sup}, sasl, safe) at pid <0.346.0>

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
          supervisor: {local,sasl_sup}
             started: [{pid,<0.348.0>},
                       {id,release_handler},
                       {mfargs,{release_handler,start_link,[]}},
                       {restart_type,permanent},
                       {shutdown,2000},
                       {child_type,worker}]

=PROGRESS REPORT==== 6-Dec-2016::10:24:44 ===
         application: sasl
          started_at: myapp@tvb
10:24:44.894 [debug] Supervisor sasl_sup started release_handler:start_link() at pid <0.348.0>
10:24:44.894 [info] Application sasl started on node myapp@tvb
10:24:45.387 [debug] Lager installed handler lager_backend_throttle into lager_event
```


## まとめ

rebarを使うとテストも依存関係の解決も簡単に行えるのでオススメです。