LoginSignup
11
7

More than 5 years have passed since last update.

elixirでmeckをつかう

Last updated at Posted at 2013-12-14

Elixir Advent Calendar 2013 15日目です。

pure elixirネタではないのですが、erlangとの相互運用性もelixirの良さということで、ご了承を。

meck はmockをerlangで実現するモジュールですがelixirでも使えます

使いかた

自分のプロジェクトのmix.exsに

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へのプロキシだとして、こんなかんじに
実装したとします。

target_module.exs
    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がありますのでそこで初期化をします。

target_module_test.exs
        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をモックにしたてて、終了するとアンロード
するようになります。あとは、ガシガシテストを畫いていきます。

elixir
      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 さんです!

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7