Elixir
PowerAssertEx
ElixirDay 19

Elixir 言語における Power Assert の紹介

More than 1 year has passed since last update.

Power Assert とは

失敗結果の表現力が向上した assert とでも言えばよいでしょうか。

例えばこんな感じの ExUnit によるテスト失敗結果が、

before
  1) test Enum.at should return the element at the given index (PowerAssertSampleTest)
     test/power_assert_sample_test.exs:5
     Assertion with == failed
     code: array |> Enum.at(index) == two
     lhs:  3
     rhs:  2
     stacktrace:
       test/power_assert_sample_test.exs:7

こんな感じに表示されるようになります。

after
  1) test Enum.at should return the element at the given index (PowerAssertSampleTest)
     test/power_assert_sample_test.exs:5
     array |> Enum.at(index) == two
     |             |  |         |
     |             3  2         2
     [1, 2, 3, 4, 5, 6]
     stacktrace:
       test/power_assert_sample_test.exs:7

ぱっと違いが分かりにくいのですが、diff にすると差分が分かりやすいです。

diff
-     Assertion with == failed
-     code: array |> Enum.at(index) == two
-     lhs:  3
-     rhs:  2
+     array |> Enum.at(index) == two
+     |             |  |         |
+     |             3  2         2
+     [1, 2, 3, 4, 5, 6]

このような形で途中式の計算結果も合わせて表示してくれる機能です。
通常の assert は単に失敗していますと表示されるだけですが1、Power Assert はそこに至るまでの経緯が表示されるためテスト結果の間違いを探しやすく、理解の助けになってくれます。
個人的には新しい言語を学ぶときに Power Assert があると言語の習得が早くなるだろうなと考えています。新しい言語を学ぶときには、何でこのコードで動かないのか?という問題に当たることも多く、その際に test を書いて途中経過を知ることができたらきっと理解しやすくなると思われるためです。

Power Assert 自体をもっと詳しく知りたい方は以下の資料を参照してみてください。JavaScript 版 power-assert 作者の t_wada さんによる資料です。

power-assert in JavaScript

Elixir での Power Assert とは

Power Assert の Elixir 版実装が PowerAssertEx です(以下 Elixir 版について触れるときは PowerAssertEx と記載します)。

Elixir の標準テストフレームワークである ExUnit をベースとしており、標準の assert マクロを置き換えることで Power Assert 機能を提供します。

ポリシーとしていつでも元の assert に戻せるようにすることを目指しています。これは JavaScript 版 power-assert がそういったポリシーを持っており、それが cool だと思ったので真似しています。いつでも戻せるということは採用もしやすいですよね。

使い方

前提条件

Elixir 標準テストフレームワークの ExUnit に依存しています。ExUnit を用いたテストであれば、容易に採用することができます。特にテストフレームワークを変えずに使っていれば ExUnit を使っていると思うので大抵の場合は使えると思います。

インストール

プロジェクトの mix.exs ファイルに以下を追加して、mix deps.get コマンドを実行するだけです。

mix.exs
defp deps do
  [
    {:power_assert, "~> 0.0"}
  ]
end

PowerAssertEx を有効にする

テストコードで use ExUnit.Case としている箇所を use PowerAssert とするだけで有効になります。

your_awesome_test.ex
defmodule YourAwesomeTest do
-  use ExUnit.Case
+  use PowerAssert
end

あるいは ExUnit.CaseTemplate を使ったテストコードを書いているかもしれません。その場合は using macro を定義して quote ブロック中に use PowerAssert を追加してください。

your_awesome_test.ex
defmodule YourAwesomeTest do
  use ExUnit.CaseTemplate
+  using do
+    quote do
+      use PowerAssert
+    end
+  end
end

これで完了です。Happy Testing! :muscle:

制限

マクロに対するテストで落ちることがある

マクロに対するテストコードで Power Assert を使うと落ちることがあります。
PowerAssertEx の AST 変換処理がマクロをしっかり考慮できていないためです。
表示がおかしくなるとかではなく、テストが途中でとまってしまうので、制限と書いています。
これもできればしっかりしたいなと思いつつ出来ていません。一時的に回避する場合は ExUnitassert を使えばよいです。

今後

制限の部分をもう少し緩めの制限にすることができたら 0.1.0 とか出したいなと考えています。ただあまり計画を立てるのは得意ではないので、いつになるやらという感じです。

どちらかというと今の時点のコードでもう少しユーザを増やしていきたいなと考えています。もし良ければ使ってみて、フィードバックなどいただければと思います。あとスターつけてくださいお願いします。

ちなみに今度の Elixir Meetup #1 in Drecom で Power Assert について話してきます。この記事の内容は初心者向けでしたが、このイベントでは inside PowerAssertEx 的なマニアックな話をしたいなと考えています。

参考リンク


  1. Elixir の ExUnit は lhs, rhs の計算結果までは表現してくれるので、Power Assert っぽいところがあり単に失敗していると言うと少し語弊がありますが。