Elmでもテストを書こう
Elmでテストを書きましょう。
この文章はElm2 Advent Calendar 2017の22日目の投稿です。
昨日は @cyclone_t さんの YouTube IFrame Player APIをElmから利用する でした。
対象者
Elmのテストに興味がある人が対象になります。
ある程度、他言語でユニットテストを書いたことがある方が望ましいです。
わかること
- Elmのテスティングライブラリはどんなものか
- ElmのテストをCLI上で行うにはどうするのか
- 文字列の一致をテストするElmのテストコードの書き方
それでは始めましょう
Elmのテスティングライブラリはどんなものか
Elmには他の言語同様にテストをサポートするライブラリが用意されています。
suite : Test suite = describe "The String module" [ describe "String.reverse" -- Nest as many descriptions as you like. [ test "has no effect on a palindrome" <| \_ -> let palindrome = "hannah" in Expect.equal palindrome (String.reverse palindrome) -- Expect.equal is designed to be used in pipeline style, like this. , test "reverses a known string" <| \_ -> "ABCDEFG" |> String.reverse |> Expect.equal "GFEDCBA" -- fuzz runs the test 100 times with randomly-generated inputs! , fuzz string "restores the original string if you run it again" <| \randomlyGeneratedString -> randomlyGeneratedString |> String.reverse |> String.reverse |> Expect.equal randomlyGeneratedString ] ]
elm-testがElmにおけるテスティングフレームワークです。テストコードに必要なモジュール一式が提供されています。特徴的なのはファジングを気軽に実行できることです。
ファジングとは、ソフトウェアの不具合(とくに脆弱性を意図することが多い)を発見するためのテスト手法の一つである。ファズ(英:fuzz)(予測不可能な入力データ)を与えることで意図的に例外を発生させ、その例外の挙動を確認するという方法を用いる。ファズテストと呼ばれることもある。 ファジング - Wikipedia
Fuzzモジュールはファジングを強力にサポートします。seedからランダムな入力データを自動で生成してテスト対象コードの入力とすることが可能です。
String型の生成データを例に見ても ""
, "\n"
のような罠となりそうな文字列も生成してくれます。入力を自前で用意しないといけない手間が省ける点でも利用をおすすめできます。
詳しくは Elm2 Advent Calendar で @jinjor さんが記事をかかれています。要チェックですよ!
ElmのテストをCLI上で行うにはどうするのか
Elmのテストコードはブラウザ上で結果を確認する方法とコンソール上で確認する方法が存在します。CIの都合上、一般的にはコンソール上で確認するシーンが多いと思いますのでコンソール上で実行するためのテストランナーをご紹介します。
node-test-runnner
はテストの実行をサポートするだけではなく、初期テスト環境を自動構築してくれます。
$ elm-test init Created ./src Created ./elm-package.json Created ./tests/elm-package.json Created ./tests/Example.elm Created ./.gitignore Starting downloads... ● eeue56/elm-lazy-list 1.0.0 ● eeue56/elm-lazy 1.0.0 ● eeue56/elm-shrink 1.0.0 ● elm-community/elm-test 4.2.0 ● elm-lang/html 2.0.0 ● elm-lang/virtual-dom 2.0.4 ● mgold/elm-random-pcg 5.0.2 ● elm-lang/core 5.1.1 Packages configured successfully!
テストコードが書けたらテストを実行しましょう
$ elm-test Success! Compiled 0 modules. Successfully generated /dev/null Success! Compiled 1 module. Successfully generated ./elm-stuff/generated-code/elm-community/elm-test/elmTestOutput.js elm-test 0.18.10 ---------------- Running 1 test. To reproduce these results, run: elm-test --fuzz 100 --seed 96181054 TEST RUN INCOMPLETE because there is 1 TODO remaining Duration: 242 ms Passed: 0 Failed: 0 Todo: 1 ↓ Example ◦ TODO: Implement our first test. See http://package.elm-lang.org/packages/elm-community/elm-test/latest for how to do this!
テスト結果に注目してください。Todoがあるのがわかると思います。これはテストコード内にtodoを設定しておくことができるAPIが提供されているためです。 それでは実際にテストコードを書いてみましょう
Elmのテストコードの書き方
elm-test init
で作成された Example.elm
を少し変更してみます。
module Example exposing (..) import Expect exposing (Expectation) import Fuzz exposing (Fuzzer, int, list, string) import Test exposing (..) suite : Test suite = describe "Example Test Suite" [ test "should equal" <| \() -> description |> Expect.equal "Elm is functional language." ] description : String description = "Elm is functional language."
これは文字列 Elm is functional language.
の一致をテストしています。
describe
は Test
モジュールが提供しています。これを利用するとテストの名前をつけることができます。ここで定義しておくとテスト失敗時にどのテストケースが失敗したのかがわかりやすくなるので設定をおすすめします。第二引数がリストになっているのは複数のテストケースを渡すことができるからです。テストケースは test
で実行しています。第一引数でテストケースに名前をつけています。第二引数でテストの詳細を実装します。実際には関数の実行結果をテストすることになります。
elm-test 0.18.10 ---------------- Running 1 test. To reproduce these results, run: elm-test --fuzz 100 --seed 1753188457 ↓ Example ↓ Example Test Suite ✗ should equal "Elm is functional language." ╷ │ Expect.equal ╵ "Elm is functionall language." TEST RUN FAILED Duration: 261 ms Passed: 0 Failed: 1
テストをわざと失敗させてみました。するとこのように失敗したテストの詳細がターミナル上に出力されます。
等価性を判定する Expect.equal
の他にも様々なAPIが提供されています。
より実践的なテストコードの書き方は node-test-runner
のexampleコードが参考になります。
node-test-runner/TestsFailing.elm at master
このテストコードでは先に説明したFuzzモジュールを使ったテストも体系的に学ぶことができるので、 git clone
コマンドを実行して是非手元で試してみてください。
まとめ
- elm-testを使おう
- node-test-runnerを使ってテストを実行してみよう
- Expectモジュールが提供するAPIを使ってテストコードを書こう
明日は @zaneli@github さんの Elm + native moduleでドラッグ&ドロップしたファイルの情報を取得する です!