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

## Power Assert とは

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

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

```console: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
```

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

```console: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: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 を書いて途中経過を知ることができたらきっと理解しやすくなると思われるためです。

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

Power Assert 自体をもっと詳しく知りたい方は以下の資料を参照してみてください。JavaScript 版 [power-assert](https://github.com/power-assert-js/power-assert) 作者の [t_wada](https://twitter.com/t_wada) さんによる資料です。

[power-assert in JavaScript](http://www.slideshare.net/t_wada/powerassert-in-javascript)

## Elixir での Power Assert とは

Power Assert の Elixir 版実装が [PowerAssertEx](https://github.com/ma2gedev/power_assert_ex) です(以下 Elixir 版について触れるときは PowerAssertEx と記載します)。

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

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

## 使い方

### 前提条件

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

### インストール

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

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

### PowerAssertEx を有効にする

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

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

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

```diff: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 変換処理がマクロをしっかり考慮できていないためです。
表示がおかしくなるとかではなく、テストが途中でとまってしまうので、制限と書いています。
これもできればしっかりしたいなと思いつつ出来ていません。一時的に回避する場合は `ExUnit` の `assert` を使えばよいです。

## 今後

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

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

ちなみに今度の [Elixir Meetup #1 in Drecom](http://beam-lang.connpass.com/event/23013/) で Power Assert について話してきます。この記事の内容は初心者向けでしたが、このイベントでは inside PowerAssertEx 的なマニアックな話をしたいなと考えています。


## 参考リンク

- at GitHub
  - https://github.com/ma2gedev/power_assert_ex
- at hexpm
  - https://hex.pm/packages/power_assert
- [power-assert in JavaScript](http://www.slideshare.net/t_wada/powerassert-in-javascript)
- [power-assert-js](https://github.com/power-assert-js/power-assert)
- [Power Assert in Ruby](https://speakerdeck.com/k_tsj/power-assert-in-ruby)