Elixir Advent Calendar 2013 15日目です。
pure elixirネタではないのですが、erlangとの相互運用性もelixirの良さということで、ご了承を。
meck はmockをerlangで実現するモジュールですがelixirでも使えます
使いかた
自分のプロジェクトのmix.exsに
defp deps do
[ { :meck, "~> 0.1", github: "exproxus/meck" } ]
end
を入れておきます。これでmix deps.compileすることで、自分のプロジェクトでmeckが
使えるようになります。
モックの生成
テストコード中で
:meck.new(Module, [:passthrough])
これでModuleがモックになります。:passthroughというのは、モックとして仕込ん
でいない関数が呼ばれたときには本物を呼び出すという指定です。passthrough以外
にもいろいろ指定できるが詳しくはmeckのドキュメントを参照のこと。このままだと
ただ本物にフォワードするだけのモックなので、芸を仕込みます。
作られたモックに芸を仕込むのはいくつかの方法があります。
方法1
:meck.expect(Module, :func, fn(args) -> returnvalue end)
関数funcに対して無名函數を対応付ける方法でです。自由度が高いが煩雑に
慣るきらいがあるので通常はもっとベタな方法をつかいます。
方法2
:meck.expect(Module, :func, [{args_spec1, ret_spec1},
{args_spec2, ret_spec2},...])
関数funcに対して引数スペックと戻り値スペックペアのリストを指定する、極
めてベタな方法です。書式だけ見ると分かりにくいかもしれないけど、実際に
書いてみると、あとで示しますが、極めてベタ。
引数スペックは、値を書けばよいが、簡単なパターンマッチも使えます。
任意の値を示す"_"
はアトムとしての:"_"
を用います。
戻り値スペックは、戻値の他に例外の発生などで以下の形式となります。
ret_spec -> :meck.val(exp) expを返す|
:meck.seq(list) listの要素を順に返す|
:meck.loop(list) listの要素を繰り返して返す|
:meck.raise(:throw, reason) 例外を発生|
:meck.raise(:error, reason) エラー例外を発生|
:meck.raise(:exit, reason) exit例外を発生
例:
TargetModuleモジュールを開発することがお仕事だとします。
TargetModuleは単純なExternalModuleへのプロキシだとして、こんなかんじに
実装したとします。
defmodule TargetModule do
def priv(a) do
ExternalModule.priv(a)
end
end
しかし必要なExternalModuleモジュールはまだまだ開発されていなくて、
仕様だけが決っているとします。
ExternalModule.priv/1の仕様として
ExternalModule.priv("a") -> "a"
ExternalModule.priv("b") -> "b"
ExternalModule.priv({_, _}) -> "badmatch"
ExternalModule.priv(_) -> "c"
だとします。
このような場合にTargetModule開発者のexunitはどうなるでしょうか。
exunitにはsetupとteardownがありますのでそこで初期化をします。
setup do
:meck.new(ExternalModule, [:passthrough])
:meck.expect(ExternalModule, :priv,
[
{["a"], :meck.val("a")},
{["b"], :meck.val("b")},
{[{:"_", :"_"}], :meck.raise(:throw, "badmatch")},
{[:"_"], :meck.val("c")},
])
end
teardown do
:meck.unload(ExternalModule)
end
このように、極めてベタな感じにモックの振る舞いを記述できます。ほとんど仕様そのまま
書けるのが良いですね。もちろんコレくらいなら自前でモックを作っても同じなのですが。。。。
これでexunitを動かすとExternalModuleをモックにしたてて、終了するとアンロード
するようになります。あとは、ガシガシテストを畫いていきます。
test "priv-mock" do
assert("a" == TargetModule.priv("a"))
assert("b" == TargetModule.priv("b"))
assert("c" == TargetModule.priv("c"))
assert("c" == TargetModule.priv("d"))
assert catch_throw(TargetModule.priv({"d", "a"})) == "badmatch"
end
こんなかんじです。
つぎは、@alucky0707 さんです!