Elixirの特徴のひとつとして、柔軟なパターンマッチが挙げられます。本記事では、ExUnitを用いたテストにおいて、複雑なデータ構造をパターンマッチを用いて効率的にテストする方法を見ていきます1。
ExUnitのassert
について
ExUnit.Assertionsのassert/1のマニュアルにもある通り、assert
ではtruthy
/falsy
をテストするだけでなく、パターンマッチもテストできます。
Similarly, if a match expression is given, it will report any failure in terms of that match. Given
assert [1] = [2]
you'll see:
match (=) failed code: assert [1] = [2] left: [1] right: [2]
これを使って、複雑なデータ構造をテストしてみます。
複雑なデータ構造のテスト
たとえば、SlackのAPIで取得できるような、ユーザを表すデータ構造をテストしたいとします。以下は、SlackのAPIドキュメントを参考にして例を作りました。
ExUnit.start()
defmodule UserTest do
use ExUnit.Case, async: true
setup_all do
%{
id: 123_456,
name: "あんちぽ",
profile: %{
real_name: "栗林健太郎",
image: %{
original: "https://0.gravatar.com/avatar/23f4d5d797a91b6d17d627b90b5a42d9?s=1000",
thumbnail: "https://0.gravatar.com/avatar/23f4d5d797a91b6d17d627b90b5a42d9?s=24"
}
}
}
end
test "`user`のデータ構造をテストする", user do
assert %{id: _, name: "あんちぽ", profile: profile} = user
assert %{real_name: "栗林健太郎", image: image} = profile
assert %{original: _, thumbnail: _} = image
end
end
setup_all
にあるのは、テストしたいデータ構造です。その下のtest
マクロ内で、実際のテストを行っています。テストしたいデータは、APIから返ってきたJSONをElixirのマップにデコードしたものだとおもってください。
このデータは三階層になっています。このような深いデータ構造をテストしようと思うと、階層ひとつひとつをマップのキーを使って参照したり、別の変数に1行ずつ束縛したりしながらテストしていくみたいな感じにもなりそうです。
上記のコードから、アサーションの部分のみを抜粋します。
test "`user`のデータ構造をテストする", user do
assert %{id: _, name: "あんちぽ", profile: profile} = user
assert %{real_name: "栗林健太郎", image: image} = profile
assert %{original: _, thumbnail: _} = image
end
こうやって書いていくと、パターンマッチにより構造をテストしつつ、下位の階層を変数に束縛して次のアサーションに用いるということで、少ない記述でとてもわかりやすく複雑なデータ構造のテストが実現できていますね。
おわりに
本記事で書いた内容は、Elixirに慣れたひとや、関数型言語的なパターンマッチに詳しいひとからすると当たり前かもしれませんが、個人的に感心した内容だったので、書いておきました。
-
この記事で書いている方法は、pelemay/analyzer_test.exs at master · zeam-vm/pelemayのコードを見ていて、そんな書き方があるのかと知りました。 ↩