Mock を使ったテストを覚えたので、そのメモです。
全容は GitHub に置いてあります。
リポジトリ名は test_with_mocks_ex
ですが、中の Elixir のプロジェクト名 は fizz_buzz
になってます。気をつけて。
プロジェクトで定義している唯一の関数 FizzBuzz.fizz_buzz/1
は、数値を与えると FizzBuzz します。
実態はコマンド fb
を呼び出しています。fb
は各自実装してください。
…というわけにもいかないので、fb
コマンドの呼び出しをモックします。
パッケージの追加
"Mock" というパッケージを利用します。
mix.exs
に依存関係を追加します。
defp deps do [ {:mock, "~>0.3"} ] end
パッケージの取得。
$ mix deps.get
テスト対象
テスト対象の関数はこのようになっています。
関数 FizzBuzz.fizz_buzz/1
は System.cmd/2
を使ってコマンド fb
を実行し、結果を返します。
defmodule FizzBuzz do @moduledoc """ Documentation for FizzBuzz. """ @doc """ 外部コマンド `fb` を利用して FizzBuzz する。 `fb` コマンドは各自実装してください。 """ def fizz_buzz(n) do {res, _} = System.cmd("fb", ["#{n}"]) res end end
テストを書く
モジュールの import
タイプする手間を減らすために、テスト対象のモジュール FizzBuzz
と、モックを提供するモジュール Mock
を import しておきます。
defmodule FizzBuzzTest do use ExUnit.Case import Mock import FizzBuzz ... end
モックで囲って assert する
マクロ Mock.with_mock/4
はモックするモジュール、モックする関数、およびブロックを取り、モックする関数がブロック内から呼び出されたときに本来の関数に代わってマクロに与えた関数が呼び出されます。
test "fizz_buzz 1" do with_mock System, cmd: fn "fb", ["1"] -> {"1", 0} end do assert fizz_buzz(1) == "1" end end
# | 引数 | 値 |
---|---|---|
1 | モジュール | System |
2 | オプション | (ここでは省略) |
3 | モック | [{:cmd, fn "fb", ["1"] -> {"1", 0} end}] |
4 | ブロック | do assert fizz_buzz(1) == "1" end |
モックはキーワードリストになっているため、[cmd: fn "fb", ["1"] -> {"1", 0} end]
と書くことができます。さらに引数では括弧 []
を省略できるため、上記のようは記述になっています。
FizzBuzz.fizz_buzz(1)
は内部で System.cmd("fb", ["1"])
という形で呼び出しているので、モックする関数でその形に合わせた引数と戻り値を用意しています。
複数のモックで囲って assert する
マクロ Mock.with_mocks/2
は、モックをリストにして複数定義できます。
なのですが。ここではモックできる関数を用意できていないので、リストの要素は一つになっています。
test "fizz_buzz 3" do with_mocks [{System, [], [cmd: fn "fb", ["3"] -> {"Fizz", 0} end]}] do assert fizz_buzz(3) == "Fizz" end end
Mock.with_mocks/2
に与えているモックのリストの要素 {System, [], [cmd: fn "fb", ["3"] -> {"Fizz", 0} end]}
は Mock.with_mock/4
と同じ構成になっています。
ただ、引数でないので省略した記述ができないのでオプションの空リストやモックの括弧も記述しています。
setup でモックする
モックを setup で定義します。
Mock.setup_with_mocks/2
のモックのリストの書式は Mock.with_mocks/2
と同じです。
ブロックの戻り値の扱いは ExUnit.Callbacks.setup/1
と同じです。ですので特に返す値がない場合は :ok
を返します。
コンテクスト変数を受け取れる ExUnit.Callbacks.setup/1
に対応する Mock.setup_with_mocks/3
もあります。
describe "case 5" do setup_with_mocks [{System, [], [cmd: fn "fb", ["5"] -> {"Buzz", 0} end]}] do :ok end test "fizz_buzz 5" do assert fizz_buzz(5) == "Buzz" end end
test でモックする
テスト定義でモックを記述します。
モックの記述の書式は Mock.with_mock/4
と同じです。
こちらもコンテクスト変数を受け取れる Mock.test_with_mock/6
が用意されています。
test_with_mock "fizz_buzz 9", System, cmd: fn "fb", ["9"] -> {"Fizz", 0} end do assert fizz_buzz(9) == "Fizz" end
呼び出されたことを確かめる
マクロ Mock.call/1
は、引数に与えたモックされた関数の呼び出しがあったばあいに真を返します。
test "fizz_buzz 15" do mock = fn "fb", ["15"] -> {"FizzBuzz", 0} end with_mock System, cmd: mock do assert fizz_buzz(15) == "FizzBuzz" assert called(System.cmd("fb", ["15"])) end end
いつか読むはずっと読まない:三部作、再び
いままで読んできたシリーズものでも邦訳が完結してないのが多いんだよな…。「恐竜惑星(惑星アイリータ調査隊)」とか「ダーコーヴァ年代記」とか「ワイルド・カード」とか…。
「〈クロノス・クロニクル〉第一弾」?
…またシリーズものに手を出してしまった。
- 作者: マリー・ルツコスキ,圷香織
- 出版社/メーカー: 東京創元社
- 発売日: 2010/11/27
- メディア: 文庫
- クリック: 3回
- この商品を含むブログ (4件) を見る
- 作者: マリー・ルツコスキ,圷香織
- 出版社/メーカー: 東京創元社
- 発売日: 2011/02/12
- メディア: 文庫
- クリック: 4回
- この商品を含むブログ (2件) を見る
それから何年経っただろうか。〈クロノス・クロニクル〉第 3 巻はいまだ刊行されていない。
そして。
本書はパテルの小説の処女作であるが、二〇一五年三月に出版されたのち同年七月には続巻の Cities and Thrones が出版され、今年(二〇一七年)には本三部作の最終巻となる The Song of the Dead が出版された。
(本書のあとがきより)
…またシリーズものに手を出してしまった。
- 作者: キャリー・パテル,Kanehira K,細美遙子
- 出版社/メーカー: 東京創元社
- 発売日: 2017/07/28
- メディア: 文庫
- この商品を含むブログ (2件) を見る