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でドラッグ&ドロップしたファイルの情報を取得する です!
フロントエンジニアとしてUXに触れる取っ掛かりとしてのコンテキスト
この文章はFringe81 Advent Calendar 2017の7日目の投稿です。 また、この文章で述べる考えはUX DAYS TOKYO主催のワークショップ、コンテキストの理解と実践に基づいております。
対象者
- フロントエンジニアだけどUXという言葉しか知らない方
わかること
- コンテキストはUXの一端を担っている
- コンテキストの種類
それでは始めましょう
UXって何?
はじめにこの文章中でのUXについて定義してみます。 この文章中でのと断ったのには理由があります、UXという言葉は色んなシーンや人によって解釈されていますが、決まりきった定義がないと私が考えているからです。 気になる方は情報を収集してみてください。山ほど資料が見つかるはずです。
UX = ユーザーのタスク + コンテキスト
今回はこのようにUXを解釈します。みなさんがサービス開発者であればユーザーは何らかの目的をもってサービスを利用するはずです。ここで言うところのユーザーのタスクとはこのことを指しています。ユーザーのタスクとコンテキストがあってUXとなります。次にコンテキストについてお話します。
コンテキスト
エンジニアであればよく議論の最中にもコンテキストという言葉を連呼されている方もいるのではないでしょうか。UX世界でのコンテキストとは「状況」「環境」「前後関係」のようなユーザーのタスクを達成する上で影響を与える事柄と捉えます。
例えば、上記の画像からコンテキストを読み取ると、片手でデバイスを操作、一人、うつむきながら、といったことがコンテキストに当たります。移動していそうと言うような事実ではない想像の域を出ないものはコンテキストと考えないこととします。
普段開発しているときにはついつい画面の中だけで完結しがちですが、コンテキストを考慮するとより広いユーザー体験を設計できるようになります。
コンテキストの種類
コンテキストには7つの種類があるとCennydd Bowles氏の記事で語られています。
- デバイス
- 環境
- 時間
- 行動
- パーソナル
- 場所
- ソーシャル
特に一番気になるのは5の個性についてです。
スマートフォンを常にユーザーと共に、あるいは少なくとも手の届くところに置いていると親しみを感じ、そこにパーソナルスペースを見出します。(中略) それらは、私たちが秘密を打ち明けられるパートナーであり、かつ超人的な力を与えてくれるツールなのです。(中略) このパーソナルスペースが侵害されると、人は強く反応します。煩わしいSMSのスパムを誰もが嫌がることや、掲示板での痛烈な議論もそうです。。ポータブルデバイスを利用したサービスを考えているならば、ユーザーがそれを使おうと選択した時点で、こうしたパーソナルな性質があることを考えるべきです。 コンテキストを理解する(切り口-5: パーソナル) | UX TIMES
パーソナルスペースは人が持つ安全圏のことであり、一般的に親しい人ほど近くにいても嫌悪感がないことが多いです。公共交通機関で間隔を開けたがる人が多いのはこの性質によるものと考えられます。 普段私達が使うデバイスもパーソナルスペースに侵入している以上ある程度心を許す存在です。PCよりもスマートフォンの方が常に持ち歩いている分、親しい感じがするのではないでしょうか。
当然親しい存在のスマートフォンで運用するサービスもユーザーのパーソナルスペースにいることに気を配るべきだと思います。必要のない通知を送っている場合などは気をつけたほうがいいかもしれません。
実は、コンテキストはここで紹介されている7つだけではないです。 常に想像を膨らませて各々のサービスでのコンテキストを考えてみてはいかがでしょう。
コンテキスト全部盛りでサービス作ればいいの?
コンテキストは必ずしも盛り込まないといけないものではないです。コンテキストがユーザーの行動に特に影響ない限りコンテキストの優先順位は高くありません。大切なのはサービス開発する上で、どのコンテキストを選択するのか (大切にするのか) ということです。 コンテキストが変わらないものでない以上、時代やシーンに合わせて考え直していくことが重要です。
まとめ
- コンテキストとはユーザーのタスク達成に影響する様々なこと
- コンテキストを考慮すると画面外のことにまで気に出来るようになる
- コンテキストには7つの代表的な切り口が存在する
- コンテキストの切り口はもっと存在する
- どのコンテキストを選択するのかが大切
転じてElm meetup Tokyo #4に参加してElmerたちと触れ合った
突然ですが転職しました。8月からFringe81という会社で働いています。で、Elmを使い始めました。
最近ようやくElmの入り口に立ったばかりですが、Elm meetup Tokyo #4について記事にしてみます。
セッションは全部で3つとLTがいくつかありました。場所は弊社Fringe81だったので自分は運営に徹していましたが所々で参加できました。
複雑化するUIにどう立ち向かうか
セッション2つ目は@arowM_さんの発表でした。 まず前提としてElmと他のAltJS言語との大きな違いについて ElmはWebフロントエンドをかんたんにすることが目的 だと語られていました。 言語としての完成度 (あれこれ構文が揃っているとか) を第一で考えているのではなくあくまで必要なら構文は用意するというのが言語的特徴らしいです。 実際、Elmは学習コストが少なく目立ったハマリポイントもあまりないような気がしてます。 加えて、直感的に書いてしまったところではElmコンパイラに度々指示を受けることもしばしばですが、実行時エラーは驚くほど少ないです。 ここで語られてた背景を情報ソースとともに教えてほしかったのですが残念ながら機会がありませんでした。 後大切なこととして、手段にこだわらず、目的にあった最適な方法を取るべし とおっしゃっていました。 Elmでアーキテクチャを考える時には簡単な舞台が用意されています。その舞台でどういう方法を取るかは課題によって変えることができると感じているのでガッチリハマった気がします。
次に話題はCSS関連の話に移ります。CSS ModuleはElmでも人気のトピックらしく懇親会でもレスポンシブルへの対応をどうやっているのかと言う感じの知見を求める声を聞きました。論点はCSSとどう付き合っていくかということが多かった気がします。
elm-css
やstyle-elements
の良し悪しははっきりと別れていないみたいです。どちらにしてもelm-css
は一つの解決策ということで紹介されていた印象です。
複雑化するUIにどう立ち向かうか - Elmの思想に学ぶ現代的なWebフロントエンド開発手法
スライドもElmで作られています!
実プロダクションで使った話
セッション3つ目は@jshosomichiさんの発表でした。 Elmはいい先生になるという言葉の通りElmを使うことでElmから学ぶことが多かったと語っておられました。 思考に注力させてくれる事が多くなって、Elmの型定義からPure JSで例外にするべきパターンが見えてくると言っていたのが刺さりました。 まさに今自分は学んでいる途中なのですが、新しい視点を得られて明日からのコーディングの楽しみが増えるセッションでした。
懇親会
なんと懇親会の出席率は100%でした。 様子はと言うと皆さん自分が考えているプロダクトの話やアイディアについての話、知見を求める話やElmの将来の話など多岐にわたっていた印象です。 話す内容が一つのトピックに集中しない感じがまだまだ発展途上のElmらしさでしょうか。
終わってみての感想
Elmはまだまだ日本では利用者が少ない言語です。 懇親会でも関数型プログラミングの基礎についてといった初学者向けの話はあまりなく、すでにHaskellやElixirを習得している方も多くいらっしゃいました。 JSerの方がいきなり入りにくい土壌ではないのでどんどん入ってきてほしいと思います。一つのムーブメントが起きてElmがもっと日本中で使われるようになり議論が活発化してElmの未来を大いに語れる日が来ると良いなあ。 これからもElmと付き合うことになると思うのでまたアウトプットしていこうと思います。ではノシ
Domain Driven Design (DDD) Quickly を読み始めた
DDD Quickly
ひょんなことからDDDについて学ぶことになりまして、無料で配布されているDDD Quicklyの日本語訳バージョンを読み始めました。 あの分厚い書籍とは異なり80ページ強なので割りと読みやすそうです。
ドメイン駆動設計とは何か
ソフトウェアの設計手法は数あれどドメイン駆動設計 (DDD) は聞いたことはあったものの知りませんでした。
個人的に設計は苦手分野でありインプット状態はこんな感じだと思います。
- オブジェクト指向設計 (クラス図…etc.)
- 凝縮度や結合度
- モジュール性 (単一の役割になっているか)
- MVC
本書ではまず、ソフトウェアは現実世界の問題を解決するものであり、そのためにはそのドメインに精通した知識が必要と言っています。 ドメインをうまく汲み取りソフトウェアに反映できれば最高そうですね。そのためにはドメインをうまく反映したモデルが必要です。
ドメイン -> 抽象 (モデル)
これによってエンジニアはドメインに関する知識がなくてもドメインモデルさえあれば優れたソフトウェアを作ることができます。
モデルはドメインの専門家の知識を取捨選択しながら作ることになります。
ドメインモデルはプロジェクトの関係者に向けて表現したもので、共通の言語でやり取りできるということですね。
ドメインの専門家から話を聞く機会が多々ありますが、こんなにうまく取捨選択できたことないので未だ半信半疑だったりします。
ウォーターフォールやアジャイルなどのソフトウェア開発手法と比べられていましが、同じ土俵で比べるものなのか疑問でした。でも、設計過剰や実装過剰といった話は想像しやすかったですね。
ドメインモデル作成の流れはコミュニケーションの参考になりました。こんなにうまく必要なだけの情報を引き出せるようになりたいですね。
ここまで読んで
あまり特別なことはしてない印象を受けました。
要件を聞いて設計するのはいつもやっていることですし、そのために必要な情報を引き出すこともやっています。
でもですね、それをうまくプロジェクト関係者が共有できているのか、引っ張り出す必要のない知識まで設計に反映してないか。といった問題があることがわかりやすく書かれていたので、是非続きを読んでいこうと思います!
Jenkins 2.0おじさんとレガシー環境を駆け出していく
境遇
レガシー環境で働けているので改善活動が捗る今日この頃。 自動テストをいよいよ実行したいのでCIサポートしてくれるツールを考えてみた。 オンプレの制限があったのでJenkinsがまっさきに思い浮かんだがあまり導入にポジティブじゃなかった。 TECHNOLOGY RADARを読んであまりいい印象じゃなかったから。 とは言え課題のコード品質アップにCI環境は必須だ。 ちょうどpepaboが採用している記事を見かけたのでDrone.ioも検討したが、そのままでは利用できないのでplugin作ろうとしたが、あまりマイナーに突っ走ると周りがついてこれなくなると思ったので断念した。 そんなこんなで結局JenkinsおじさんとCIデビューすることになった。
インストール
サーバは適当に用意してOSにはCentOS7を採用した。 JenkinsはMasterとNode×2の構成にした。 Masterには実際のビルド実行環境を用意せず、Nodeにまかせることにした。 Masterの作成にはAnsible Playbookを作成した。 JenkinsのRoleは充実していてGalaxyで配布されているものをそのまま利用した。
NodeにはSSHとGitとJDK 1.8を最低限インストールしておき、Masterからの認証ができるようにしておく。 SSHを使ったNodeの追加はプラグインでサポートされているのでMasterにプラグインを導入するのを忘れずに。 JDK 1.8なのはJenkins 2.54からJava8が必須になったから。
あとはNodeにビルド環境を構築すればおっけー。
Pileline as a code
CI環境を用意したらビルド時に行うこと、つまりPipelineを登録する。 Jenkins 2.0からはJenkinsfileをつかった登録方法が一般的だ。 Jenkinsfile用意してリポジトリ上で管理するとチームメンバに嫌でも目に入るし、何をやっているのかもなんとなくわかってもらえると思う。 Jenkinsfileの構文はとても簡単にできるDeclarative Pipelineとより高度な設定を記述できるScripted Pipelineが用意されている。
やっていることはBuild, Test, Deploy, Lintで今のところはDeclarative Pipelineで運用している。 実行中に失敗したらRocketChatに通知するようにしている。 Declarative Pipelineでは失敗のステータスから成功に変わった時にイベントを登録する構文が用意されていないのでScripted Pipelineで解決できるなら試してみても良いかもしれない。
Jenkinsfileはリポジトリのルートにおいておくと便利だ。
Jenkinsのジョブ作成にGitHub Organization Folderを利用すると自動でJenkinsfileのあるリポジトリを見つけてJenkinsに登録してくれる。
リポジトリにWebHookを設定しておくとPush時に自動でジョブを実行してくれてCIを実現しやすくなる。
実はうまく行っていない事例としてブランチ名を xxx/yyy
みたいにスラッシュを含むようにするとリポジトリスキャン時に登録できるもののPush時に自動でジョブを実行してくれないと言うものがある。
Blue Ocecan
Jenkinsの見た目にはMaterial Designを利用しているんだけど、近頃Blue Ocean 1.0がリリースされたこともあってインストールしている。
見た目がモダンなデザインでGUIを利用してJenkinsfileを作成したりできるみたいだけど試せていない。 今のところは見た目のおしゃれさでしか恩恵を受けられていないんだけど、チームがJenkinsへ慣れ親しんでもらうためにGUIで色々できるようにしたい。
Jenkinsの所感
たまーにリポジトリのfetchに失敗していたりして、 git fsck
や git gc
をやってやらないといけない時があるのが謎だったりするのですが、今のところ問題なくコード品質の改善に一役買ってくれている。
Jenkinsはジョブの登録が自由にできるため、CIに関係ないジョブ (ansible playbook使ったサーバ構成のジョブなど) を登録できてしまうが、CIと関係のないジョブは登録しないようにしようと思った。
というのもジョブの実行にWeb APIが使えて便利だからといろいろ登録すると、何のためにとか依存関係が不透明化したジョブがたくさんできて煩雑になりそうだからだ。
Jenkinsおじさんになんでも押し付けるのではなく解決したい課題にあったツールを模索していきたいところである。
Vagrant 1.9.4 には不具合がある
vagrant up
または vagrant reload
をするとエラーが起こる。
エラーメッセージは次のIssueを見ていただきたい。
Issueにある通り3つのファイルをmasterブランチからダウンロードして上書きすると解決する。
vagrant provison
を利用したら、さらにエラーが発生した。
vagrant plugin install vagrant-share --plugin-version 1.1.8
でバージョンアップしてあげましょう。
VagrantやVirtualBox周りはまだまだ安定しないですねー。
んー微妙。
プログレッシブウェブアプリ (PWA) はいいぞ
PWA
Progressive Web Apps (PWA) の存在を知ったのは2016年のng-japanでした。
ずっと気になっていたんですが、最近ようやくチュートリアルをやったので書き留めておこうかなって思います。
Your First Progressive Web App
日本語翻訳版もあるんですが、サンプルアプリの変更に追従してくれてないのでこちらメインで進めていくことになりました。
PWAと言うのはGoogleの提唱する新しい概念です。そのあたりの詳しい説明は記事が溢れているので見つけて読んでいただくのが良いかと思います。先のリンクではこの概念を実装する方法が具体的に提示されているんですが、やってみるとわかったようなわからなかったような感覚に襲われます。PWA実装手段の一つAppShellモデルの考え方はわかりやすいです。ユーザー インターフェースが機能するために必要な最小限の HTML、CSS、JavaScriptをオフラインでも読み込めるようにキャッシュしておき、コンテンツとなるデータを明確に分離する考え方です。
App Shell モデル | Web | Google Developers
その中でService Workerの利用を猛烈におすすめされるんですが、Service Wokerを使ったデータのキャッシュ操作がよく飲み込めませんでした。Service Wokerのライフサイクルやどういうイベントを利用できてそのイベントでは何をしているのか、知識無しで実装したことが一層わかりにくくさせていたと思います。ブラウザにもキャッシュ機能は実装されており、そちらのキャッシュを注意深く意識してコンテンツが更新されることを確かめるデバッグがやりにくかったです。
Service Workerを利用したキャッシュとの大きな違いはキャッシュできるデータをこちらが指定できることでしょうか。 またオフライン時にもキャッシュされたデータから素早くアプリを構築してユーザーに状況を問わず利用できるシーンを提供できるのはビジネスでもやる価値があると思いました。
マニフェストの利用も忘れてはいけません。マニフェストを宣言しておくとウェブアプリのインストールバナーをブラウザが表示してくれます。ホーム画面への追加はどのアプリでもできるのですがマニフェストを宣言しておくとアイコンなどをカスタマイズすることが出来ます。
新しく実装するためには Web Starter Kit を活用するのが良さそうです。また、LighthouseはPWAのチェックリストを自動でテストしてくれます (Lighthouseに実装されていないチェックリストもあります) 。 昨今のフロントエンドフレームワーク上に構築したアプリケーションなら大抵は満たせているはずです。フレームワークやアーキテクチャの良し悪しを語る上でのものの見方の一つとしてPWAを考えるのも面白いかもしれません。サンプルアプリ以外もぜひぜひ実装してみたいですね。