Posted at

Elixir, ExUnitとESpecの違いを比較する

Elixirでmix newしたときには標準でExUnitのテストが実行できる状態でプロジェクトが作成されますが、

RubyとRSpscに慣れ親しんだ人にはRSpec風にテストを書きたいと思うこともあると思います。

今回はElixirのBDDフレームワーク、ESpecを使って

みます。


ESpecの導入

まずは、mix.esxのdepsにespecを追加して、mix deps.getを行います。


mix.esx

  def deps do

[
{:ex_doc, "~> 0.19", only: :dev, runtime: false},
{:espec, "~> 1.6.3", only: [:dev, :test]}
]
end

$ docker-compose run --rm web mix deps.get

Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
earmark 1.3.1
ex_doc 0.19.2
makeup 0.8.0
makeup_elixir 0.13.0
nimble_parsec 0.5.0
New:
espec 1.6.3
meck 0.8.12
* Getting espec (Hex package)
* Getting meck (Hex package)

mix deps.getsが終了したら、mix espec.initでespecに必要なファイルを作ります。

$ docker-compose run --rm web mix espec.init

Could not find "rebar3", which is needed to build dependency :meck
I can install a local copy which is just used by Mix
Shall I install rebar3? (if running non-interactively, use "mix local.rebar --force") [Yn] Y
* creating /root/.mix/rebar
* creating /root/.mix/rebar3
===> Compiling meck
==> espec
Compiling 99 files (.ex)
warning: System.stacktrace/0 outside of rescue/catch clauses is deprecated. If you want to support only Elixir v1.7+, you must access __STACKTRACE__ inside a rescue/catch. If you want to support earlier Elixir versions, move System.stacktrace/0 inside a rescue/catch
lib/espec/example_runner.ex:254

Generated espec app
==> hello_world
* creating spec
* creating spec/spec_helper.exs
* creating spec/shared
* creating spec/shared/example_spec.exs

まだテストを記述していませんが、mix especでテストを実行して、動作することを確認します。

$ docker-compose run --rm web mix espec

Compiling 1 file (.ex)
Generated hello_world app

0 examples, 0 failures

Finished in 0.45 seconds (0.38s on load, 0.07s on specs)

Randomized with seed 288292


Specの追加と実行

Specファイルはspecディレクトリにxxx_spec.exsという形式で作成する必要があるので、

hello_world_spec.exsというファイルに以下の記述を行います。


spec/hello_world_spec.exs

defmodule HelloWorldSpec do

use ESpec
it do: expect HelloWorld.hello()
|> to(eq :world)
it do: expect HelloWorld.hello("dd511805")
|> to(eq "Hello, dd511805")
end

specファイルを作成したあとは再度mix specを実行して動作確認を行います。

$ docker-compose run --rm web mix espec

..

2 examples, 0 failures

Finished in 0.75 seconds (0.53s on load, 0.22s on specs)

Randomized with seed 222464

これでESpecの記述は完了です。


Specが失敗したときの挙動について

記述したSpecをわざと失敗させてExUnitのテストのときと得られる情報に差異があるか

確認してみます。

$ docker-compose run --rm web mix espec

F.

1) HelloWorldSpec
spec/hello_world_spec.exs:6: (inside example)
spec/hello_world_spec.exs:5: (example)
Expected `"Hello, dd511805"` to equal (==) `"Hello, dd"`, but it doesn't.
expected: "Hello, dd"
actual: "Hello, dd511805"

2 examples, 1 failures

Finished in 0.94 seconds (0.66s on load, 0.28s on specs)

Randomized with seed 237106

上記がespecの実行結果です。

そして下記が同じテストの失敗が発生するようにしたExUnitの結果になります。

$ docker-compose run --rm web mix test

..

1) test greets Hello, name (HelloWorldTest)
test/hello_world_test.exs:9
Assertion with == failed
code: assert HelloWorld.hello("dd511805") == "Hello, dd"
left: "Hello, dd511805"
right: "Hello, dd"
stacktrace:
test/hello_world_test.exs:10: (test)

.

Finished in 0.2 seconds
2 doctests, 2 tests, 1 failure

Randomized with seed 219476

どちらも同じような情報を得ることが出来ます。ExUnitだと stacktraceの情報も表示されているよう

ですが、今回の失敗のパターンでは、ここから有用な情報を得ることは出来ませんでした。


Spec実行時のdoctestについて

ExUnitはmix newでプロジェクトを作成した段階のテストのサンプルでdoctestの設定が書かれていますが、

ESpecを使う場合でもExUnitと同様の記述を追加することでespec実行時にdoctestを行うことが出来ます。


spec/hello_world_spec.exs

defmodule HelloWorldSpec do

use ESpec
doctest HelloWorld
...
end

上記の記述を追加して、mix especを実行するとdoctestも同時に実行します。

$ docker-compose run --rm web mix espec

Compiling 1 file (.ex)
....

4 examples, 0 failures

Finished in 1.05 seconds (0.83s on load, 0.21s on specs)

Randomized with seed 88580

ExUnitの場合と違って、doctestとspecの数を別に集計してくれることはないようですが、

exampleの数が増えているので、doctestもspecと同時に実行されていることがわかります。