dely engineering blog

レシピ動画サービス「kurashiru」を運営するdelyのテックブログ

データサイエンスチームの取り組み Presto勉強会

f:id:sakura818uuu:20190826152215p:plain

はじめに

こんにちは。データサイエンスチームのsakura (@818uuu) です。
クラシルの検索改善を担当しています。

データサイエンスチームでは今月 Presto勉強会 を毎日行っていました。
本記事ではその取り組みをご紹介しようと思います。

Prestoとは

Prestoとは、Amazon Athenaで使用されている分散SQLエンジンのことを指します。
※本勉強会ではPrestoで動く「SQL記法」を勉強しています。

概要

Presto勉強会の概要です。

[内容] Prestoの公式ドキュメントを読み知見を深める
[目的] Prestoの知られざる機能などを学び、開発効率化に活かす
[時間] 毎日ランチに行く前の10〜20分
[参加者] データサイエンスチームメンバー(3人〜4人)

f:id:sakura818uuu:20190826150059p:plain
こんなかんじでディスプレイに映しながら話し合いました

Presto勉強会を一言でいうと
「毎日ランチ前にチームでPrestoの公式ドキュメントを読む取り組み」です。

実施内容

f:id:sakura818uuu:20190820175434p:plain
このページの単元を一つずつ読んでいきました

上記のドキュメントに従い1日大体1章ごと進めていきました。
わからないところは実際にAthenaでクエリを書いて試しながら進めていきました。

f:id:sakura818uuu:20190826115411p:plain
json_size関数を試している図 (公式ドキュメントだけでは理解するのが難しかったため)

取り組んでみた感想

複数人で公式ドキュメントを読む機会はなかなかないので貴重な経験となりました。

一番驚いたことは、ドキュメントの読み取り一つでも自分とメンバー間に違いがあったことです。
人によって読み取り方が様々で、
「この一行からそこまで読み取ることが出来るんだ」
「このメンバーだとそういった応用例まで考えているのか」
と技術ドキュメントの読み取り方の勉強としても参考になりました。


また、いくつか今後業務で活かせそうな関数も発見することができました。

f:id:sakura818uuu:20190826113346j:plain:w300
今後活かせそうな関数などをメモしています

もちろん、bool_and()to_iso8601(x) などこれいつ使うんだと思った関数もたくさんありました笑
一通り全ての関数に目を学んだことで「Prestoで出来ること/出来ないこと」を把握できたのがよかったと思います。

おわりに

Presto勉強会で公式ドキュメントを一通り読んだことで、チーム全体でPrestoへの理解が深まりました。
この勉強会で得た知見を業務に活用していきたいと思います。


データサイエンスチームでは、Presto勉強会やサーベイチャレンジなど様々な取り組みを行っています。もしご興味があればご応募・ご連絡ください: )

www.wantedly.com

データサイエンスチームの取り組み サーベイチャレンジについて

はじめに

こんにちは。データサイエンスチームのsakura (@818uuu) です。クラシルの検索改善を担当しています。

データサイエンスチームでは今年の3月から サーベイチャレンジ という取り組みを行っています。 本記事ではその取り組みをご紹介しようと思います。

概要

サーベイチャレンジの概要です。

[内容] 論文を読み、データサイエンスチーム内で共有する
[目的] 料理に関する様々な研究を知る・様々な分野の最先端技術を知る
[作業時間] 基本的に業務時間内に実施
[共有時間] 週1回のチームMTG内で共有及び議論
[参加者] データサイエンスチームメンバー(2人〜5人)

サーベイチャレンジを一言でいうと
「みんなで論文を読み、その知見を共有する取り組み」です。

進め方

サーベイチャレンジの進め方を紹介します。

  1. 各々が読む論文をmendeleyに格納し、チーム内で共有
  2. 論文を読んだ感想をテンプレートに従って記載。(Githubのissueで管理)

f:id:sakura818uuu:20190819170507p:plain
論文を読んだ感想のテンプレート

3.週1回のチームMTGでそれぞれがその週読んだ論文について議論し合う

当初はGithubのみで管理していましたが、論文の管理が少し面倒でした。
そこで文献管理ツールのmendeleyを導入し、簡単に一括管理ができるようにしました。便利です。

実施内容

どんな内容の論文を読んでいるか少しご紹介します。

f:id:sakura818uuu:20190820091307p:plain
実際に読んだ論文1

f:id:sakura818uuu:20190820092454p:plain
実際に読んだ論文2

f:id:sakura818uuu:20190820092334p:plain
今までに読んだ論文の一部

今までに30本以上の論文を議論しています。

取り組みに抱く感想

サーベイチャレンジを始めたことで論文を読む習慣が出来ました。 一人だとすぐ飽きてしまいそうですがチームで共有し議論することで一定のペースを保てて継続出来ているのが良いことだと思います。

また、実際にやってみてサービス開発に活かせるような知見をたくさん発見できたのが驚きでした。論文というと少し学術的な方面によっているのかな・・と思っていたのですがめっっちゃ役に立つ情報が眠っています。

最後に

サーベイチャレンジをすることで継続的に最先端の技術を知ることが出来たり、様々な研究内容をチーム内で共有し議論することができています。
今後も続けていき新たな発見をしていければと考えています。

Xcode11でデバッグ機能がいい感じにアップデートされたので紹介

こんにちは!クラシルiOSアプリを開発しているknchstです。

6月のWWDC19はSwiftUIなどのサプライズもあり、とても盛り上がりましたね!様々なセッションがあったのですが、個人的にいいなと思ったのがXcode11のデバッグ機能についてです。

この記事では以下の項目について紹介します。

  • Device Conditions
  • Environment Overrides
  • Debugging SwiftUI View Hierarchies

Device Conditions

f:id:knchst:20190801105018p:plain
https://developer.apple.com/videos/play/wwdc2019/412/

Thermal state condition

Xcode11から新たに端末の発熱をシミュレートする機能が実装されました。 これにより、端末を実際に発熱させることなく温度状態によるアプリの動作を確認することができるようになります。

Xcode11のメニューのWindowDevices and Simulators内に DEVICE CONDITIONS という項目が追加されていて、ここで設定することができます。

f:id:knchst:20190801114925p:plain

設定できる項目

  • Fair(わずかに高い状態、バックグラウンドフェッチなどが延期される)
  • Serious(高い状態、CPUやGPS、Bluetoothなどの使用量が削減される)
  • Critical(かなり高い状態、あらゆるリソースが最小限になる)

上記の3つをシミュレートすることができます。

Network link condition

通信状態をシミュレートする機能はiOS端末単体ではありましたが、今後はXcodeから直接端末の通信状況をシミュレートすることもできるようになります。

こちらもThermal Stateと同様でXcode11のメニューのWindowDevices and Simulators内に DEVICE CONDITIONS という項目が追加されていて、以下画像赤枠内で設定することができます。

f:id:knchst:20190801112938p:plain

デフォルトで設定できるプロファイルも

  • 100% packet loss
  • Very poor network
  • Edge Network - poor
  • Edge Network - average
  • Edge Network - good
  • Edge Network - best
  • 2G Network - poor
  • 2G Network - better
  • 3G Network - average
  • 3G Network - good
  • 3G Network - bet
  • LTE Network
  • WiFi Network
  • WiFi Network (802.11ac)
  • DSL Network
  • High Latency DNS

と増えて使いやすくなりました。

Environment Overrides

f:id:knchst:20190801103701p:plain
https://developer.apple.com/videos/play/wwdc2019/412/

Appleのプラットフォームには表示に関する設定や様々なアクセシビリティがあり、ユーザーが様々な設定を行うことができます。例えば、

  • ライトモード & ダークモード
  • ダイナミックタイプ
  • アクセシビリティ

などの項目があります。 これらの設定が変更された時に、アプリケーションのレイアウトが崩れないかを確認する必要があります。これらをデバッグする為にEnvironment Overridesという機能が新たに追加されました。

WWDCで行われたデモが以下になります。

f:id:knchst:20190801133322g:plain

Xcodeのデバッグ領域にEnvironment Overridesというボタンが追加されていて、ここから値を変更することができます。変更はリアルタイムに反映されます。また、シミュレーター・実機でも同様に動作します。

Debugging SwiftUI View Hierarchies

f:id:knchst:20190805115656p:plain

SwiftUIが新たに加わりView Hierarchiesのデバッガーも大きくアップデートされました。

Swift Reflection

SwiftUIでは、ビューにあるプロパティが自動でインスペクトされデバッガーに表示されるようになります。実際にデバッガで確認してみると、インスペクタにProfileViewのプロパティの情報が表示されています。

f:id:knchst:20190805123941j:plain

CustomReflectable

新たに追加されたCustomReflectableプロトコルに準拠することによって、独自のプロパティをインスペクタに表示することができます。

f:id:knchst:20190805123757j:plain

SwiftUIとその他のフレームワークのView Hierarchies

SwiftUIのプロジェクトでは既存のUIKitで提供されているUIViewControllerなどのクラスが利用できます。

f:id:knchst:20190805122645p:plain

上の画像のオレンジ色の枠はUIKitで実装されてもので、青色の枠はSwiftUIで実装されたビューになります。

まとめ

SwiftUIの登場によってデバッグもリアルタイム性がでてきて、より効率的な開発ができるようになりました。またDevice ConditionsやEnvironment Overrideなどのハードの機能をシミュレートできる機能を活用することによって再現しにくいランタイムエラーをデバッグすることができるので、リリース後の予期せぬ不具合も未然に防ぐことができます。

この記事で全てを紹介できていないので、詳しく知りたい方は以下をご覧ください。

RailsのCIにかかる時間を少しづつ改善している話

はじめに

こんにちは、delyでサーバサイドエンジニアをやっている山野井といいます。

kurashiruではサーバーサイドにRailsを使用しておりテストはRspecで書かれています。
CIはgithubリポジトリへのpushをフックしてAWS CodeBuild上でテストを走らせています。
またCI上のテストはparallel_tests gemを利用した並列化を行っていて、8プロセスで動いています。

弊社ではプロダクトの品質を保つ為、CIに通らないとデプロイできないルールを設けていまして、CIが完了するまでに時間がかかるとその分デプロイまでの時間もかかってしまうので1分でも早めたい気持ちがあります。

今回はアプリケーションコードには手を加えず、AWS CodeBuild上のCIの実行時間を少しづつ改善している話をしたいと思います。

実践

まずはCIの実行時間を改善する前にどこに時間がかかっているのかを把握する必要があります。

AWS CodeBuildには以下の11項目からなるphasesという概念があります。

SUBMITTED
QUEUED
PROVISIONING
DOWNLOAD_SOURCE
INSTALL
PRE_BUILD
BUILD
POST_BUILD
UPLOAD_ARTIFACTS
FINALIZING
COMPLETED

CodeBuildが実行されると各phaseが順番に実行されるようになっています。
この中で INSTALL, PRE_BUILD, BUILD, POST_BUILDのphaseはプロジェクトのルートに置いた buildspec.ymlに独自に処理を書くことができます。
以下がその例です。

version: 0.2
phases:
  install:
    commands:
      - service mysqld start
  pre_build:
    commands:
      - bundle install
      - bundle exec rake parallel:create
      - bundle exec rake parallel:migrate
  build:
    commands:
      - bundle exec parallel_rspec spec

AWS CodeBuildでは以下の画像のようにAWSのコンソールから各phaseどれだけの時間がかかっているのかを見ることができるので、それを見ながら改善策を考えていきます。

f:id:yamanoi-y:20190627143731p:plain


弊社では PRE_BUILDフェーズで bundle install やdbの作成、migration処理を行っているのですが、ここに10分ほど時間がかかっていました。
よく見ると処理時間のほとんどがmigrationにかかっていることがわかりました。
bundle exec rake parallel:migrateコマンドはcpuのコア数分プロセスを立ち上げコマンドを実行するため、migration処理が8プロセスで実行されていました。
これを1つのDBのみmigrationを行い、migration後のダンプを取得し残りのデータベースに流し込む処理に変えることで10分ほど短縮することができました。

  pre_build:
    commands:
      - bundle exec rake parallel:create
      - bundle exec rake parallel:migrate[1]
      - mysqldump -u root -prootpassword test > dump.sql
      - bundle exec parallel_test -e 'mysql -usomeuser -psomepassword test$TEST_ENV_NUMBER < dump.sql'


Before
f:id:yamanoi-y:20190627145817p:plain
After
f:id:yamanoi-y:20190627145920p:plain


またテーブルに変更が無い限りは前回のDBの状態を用いても問題ないため、db/migrateのgitのhash値からキャッシュキーを生成し、ダンプそのものをキャッシュすることで
初回のmigration処理自体をスキップすることもできます。

pre_build:
  commands:
    - git log --pretty=format:'%H' -n 1 -- db/migrate > ~/mysql-dump-checksum
    - mkdir -p ./.mysql-dump
    - |
      if [ -e "./.mysql-dump/$(cat ~/mysql-dump-checksum).sql" ]; then
        bundle exec parallel_test -e 'mysql -usomeuser -psomepassword test$TEST_ENV_NUMBER < "./.mysql-dump/$(cat ~/mysql-dump-checksum).sql"'
      fi


次に BUILDフェーズですが
BUILDフェーズでは主にRspecの実行を行っています。
今回はアプリケーションコードに手を加えない方法で高速化することを考えていたので特に変更は加えていません。
Rspec自体の速度を改善するには、一般的に遅いテストの洗い出しや、無駄なレコードの作成を行わないこと、無駄な通信を行わないことが挙げられます。
kurashiruでもここの最適化はさほど行っておらず、改善の余地があるので適宜改善していきたいと思っています。

最後に POST_BUILDフェーズですが、ここも5, 6分ほど時間がかかっていました。
AWSのコンソールからビルドログをよく見るとCodeBuildのキャッシュ機能によるs3へのアップロードに時間がかかっていました。
まだ設定の変更を完全に取り込むことはできていないのですが、CodeBuildのキャッシュタイプをs3から最近使用できるようになったローカルキャッシュに変更することで速度改善を試みています。
CodeBuildのローカルキャッシュは今年から利用できるようになった機能で、今まではキャッシュした結果をs3へアップロードしていたものをビルドホスト内に保持することができる機能です。
aws.amazon.com

この機能を有効にすることでs3への通信コストがかからなくなるため、ここにかかる時間を0にすることが確認できています。

最後に

1つ1つが地味な改善ですが、デプロイまでの時間を短縮でき、従量課金にかかる費用も節約できるので今後も時間を見つけて改善していきたいと思います。
最後までお付き合いありがとうございました。

プロジェクト管理を自動化してみたけど思うようにはいかなかった話

こんにちは。delyでAndroidエンジニアをしているkenzoです。
今回はAndroidの話ではなく、担当している別のプロジェクトの管理をいい感じにしようと思っていろいろやったけど、そんなにうまくいかなかった。という感じの内容です。

どんな話

「他部署の依頼でコンテンツを作成するプロジェクト」の管理を自動化した結果、うまくいったこと、失敗したことについてのお話です。 f:id:kenzo_aiue:20190507144040p:plain:w250

なんで自動化したの

今回のお話の背景

弊社では開発部が他の部署から依頼を受けてLP等のWebページを作成しています。
私は他の部署と開発部内の実装者とのやりとりの間に入って管理する取りまとめのようなことをしてきました。
やっていたことをざっくり言うと、依頼を受け、実装者に依頼して、配信の準備をする。という簡単なお仕事で、元々はスプレッドシートで管理をしていました。

発生した問題

新たに高い頻度でコンテンツを作る施策の開始や、開発部側で業務委託の方に実装をお任せするようになる等、次第にやりとりや確認・管理しなければいけないことが増えていきました。
そのため、業務の中でこれらのために使うリソースが増えてきたり、抜け漏れが発生しそうな気配を感じるようになったりと、既存のスプレッドシートを手作業で確認・更新するのにも限界を感じてきました。

どんな自動化したの

GASを使って下記の処理が定期的に実行されるようにしました。

  1. コンテンツの内容等が管理されているスプレッドシートから実装に関するものを抽出
  2. githubのプロジェクトに1をtodoとしてissueを追加
  3. 関係者に共有しているカレンダーに1を追加
  4. 開発側や業務委託の方と共有しているスプレッドシートに1を追加
  5. 素材・成果物が揃ったり、配信が迫ったタイミングでslackに通知
  6. 1の変更に合わせて2, 3, 4の内容を更新
  7. その他もろもろ、、、

これらの組み合わせで、今回のプロジェクトにおける業務フローのある程度の部分を自動化することができました。
f:id:kenzo_aiue:20190507174721p:plain:w250

うまくいったこと

作業削減

issueやカレンダーへの追加や更新を手動で行うという、そこそこ手間のかかる作業(定期的にスプレッドシートを確認して内容を様々な場所に追加・更新)をなくすことができました。

抜け漏れ防止

依頼が発生したり、配信日が近くなったら関係者のslackチャンネルにbotで通知を送りました。
抜け漏れを防ぐことができそうで、日々の心配事が減りました。

うまくいきそうだったこと

やりとり減らせそう

必要なタイミングで必要な人に通知を送ることで、やりとりが減らせそうな気がしていました。

うまくいかなかったこと

想定以上の戻り作業の発生

今回行った業務フローの自動化ではそれぞれの工程が完遂されているかのチェックが抜けていたため、次の工程に進んでからの戻りが度々発生しました。
その結果、その都度手動でのやりとりを行うことになってしまいました。

業務委託先の方との信頼を築けなかった?

あまりやりとりをしていないまま、業務委託先の方とのやりとりもbotでの運用に変更してしまったために信頼を築けなかったかもしれません。
そのためか、締切が近くなっても連絡がつかず、紹介してくれた別の社員に連絡をお願いすることになってしまい、コンテンツが配信ぎりぎりの完成となってしまったこともありました。

こうしたらよかったのかも

「想定以上の戻り作業の発生」に対して

戻りを完璧に防ぐのは不可能だし、いろいろな手立てを講じる工数もあまりありませんでしたが、頻繁に発生していたシンプルなミス(素材の不足や工程忘れ)についてくらいはチェックする仕組みを入れておく必要があったように思います。
また、手順をもっと明確にして初めに伝えておくことや、困った時に参照できるものを作っておくとかでも防げた可能性はあります。
他の業務でも同じことが言えそうですが、必要な内容を伝える・参照しやすくしておくことは大事ですね。

「業務委託先の方との信頼を築けなかった?」に対して

もう少しやりとりをしたり、直接会う等して信頼関係が築けてからslack botの運用を始めるべきだったと感じました。
または、slack botでの通知はあくまで自分へのリマインドにとどめ、やりとりは自分で行う。といった方法を検討してもよかったのかもしれません。
信頼関係のないままslack botだけで完結させようとしてしまったため、なにかあった場合に気軽に言ってもらえるような心理的安全性を高められなかったのではと思います。
このような信頼関係は人と関わる業務全てにおいて大切なことだと思いますが、今回は効率を求めて自動化を進める上でおろそかにしてしまったのが反省点です。 f:id:kenzo_aiue:20190507173532p:plain:w300

まとめ

自動化によって便利になることはいっぱいあるので、これからもどんどんやっていこうと思います。
ただ、自動化するのが難しい部分も多々あると思います。もちろんそこは手作業等で補う必要があるのですが、ともすると、省いたかたちで自動化してしまうこともあるかもしれません。
そうすると、せっかく自動化したのに後々工数を取られたり、ミスに気付かないまま進んでしまうということも考えられます。
特に、今回私がミスった信頼関係における部分、人間的なコミュニケーションを省いてしまうと、理論的にはうまくいきそうに思えても、どこかで綻びが生まれて問題が起きることがありそうです。
業務を効率化する時でも、あえて人の手を介した方がうまくいくこともあると思うので、その辺りを頭に入れつつ今回の反省を活かし、これからも自動化を進めていきたいと思います。

Search Engineering Tech Talk 2019 Spring に登壇しました #searchtechjp

f:id:sakura818uuu:20190426185916p:plain

こんにちは。開発部のsakura(@818uuu)です。

2019年4月23日に開催されたSearch Engineering Tech Talk 2019 Springに登壇させていただきました。

会場は南青山にあるNAVITIMEさんで行われました。
NAVITIMEさんにははじめて行ったのですが、すごくきれいな会場で発表しやすい環境でもあったのでとても助かりました。
ありがとうございました!

あとお茶もらいました。ありがとうございます。

登壇は久々だったのですごく緊張していました。
25分枠だったのでタイムスケジュールのことがものすごく不安でした。

登壇は3名いて私は3番目の発表予定でした。
一人目はNAVITIMEさん、二人目はFessを作ってる方の発表でした。
どちらのサービスもばりばり使ったことあって好きな検索サービスだったので、そんな検索の中の人と一緒の舞台にたてるのが嬉しかったです。

お二人の発表資料はこちらになります。

www.slideshare.net

www.slideshare.net

お二人の発表が終わり緊張は頂点でした。↓登壇直前のツイート

登壇はなんとか無事終えることができました。(よかった……)
すごく話しやすい場を聞いてくださる方が作ってくださったので話しやすかった&楽しかったです。
本当にありがとうございました。

登壇資料はこちらになります。

登壇を終えてハッシュタグを拝見させていただいたのですが色々な感想をつぶやいてくださり本当にありがとうございます。

#searchtechjp - Twitter Search

懇親会でもたくさん検索のお話ができてよかったです: )
すごくすごく貴重な経験をさせていただきました。ありがとうございました!

1px の変化も見逃さない!ビジュアルリグレッションテスト導入で快適フロントエンド開発

こんにちは!dely でフロントエンドの開発をしています @all__user です。
今回は kurashiru のフロントエンド開発に導入されたビジュアルリグレッションテストについてご紹介したいと思います。

【反応を多くいただいた点について記事の最後に追記しました】

目次

ビジュアルリグレッションテストとは

ある変更を加える前後でスクリーンショットを作成し、それらを比較することで意図しない挙動が無いかを検証するテスト手法です。
最終的に描画されたピクセルに少しでも変化があれば検出することができるため、技術スタックを選ばず包括的にテストできます。
検出できるものは限られていますが、スタイルのチェックはもちろん、機能や動作を検証するためのテストとしても非常に優れています。

  • 検出できるもの⭕
    • お気に入りボタンが表示されない
    • 2ページ目以降がエラーページになる
    • 画像が荒い
  • 検出できないもの❌(検証に向かないもの)
    • お気に入りボタンがクリックできない
    • 2ページ目以降へ遷移できない
    • 画像のalt属性が設定されていない

現在の kurashiru のように Rails のテンプレートと Vue を併用しているような状況でも、全体をカバーできます。技術スタックの移行フェーズでは特に効果を発揮すると思います。
テストはどこにどれだけのコストを割くかというバランスが非常に難しいと感じます。
まず最初に導入するテストとしてビジュアルリグレッションテストはとてもおすすめです。

導入の背景

kurashiru は Rails アプリケーションとして Sprockets + Slim + jQuery + CoffeeScript + SCSS で開発されてきました。
ログイン機能の開発をきっかけに部分的に SPA (Single Page Application) を導入し、現在は Webpacker + Vue + TypeScript + SCSS での開発へと移行中です。

tech.dely.jp

そのようにして SPA 化を進めていく中で、機能開発が優先され、なかなかテストに手を付けられないという状況が続きました。
すでにある Capybara + RSpec によるテストケースだけでは、SPA 部分の開発が既存部分へ与える影響や共通モジュールを変更する際の影響の検知を担保できず、テストケースを増やして対応しようとすると、非常にコストがかかるだろうと考えられました。

なんとかしなければと思いつつ開発は続き、規模が少しづつ大きくなるにつれ、手動テストでの動作検証コストが無視できない大きさになってきました。
そして、今まさにテストが必要というところまで来ていると判断しビジュアルリグレッションテストを導入することにしました。

フロントエンドのテスト?

フロントエンドのテストは難しいです。
一口にテストと言っても何を検査し担保したいのかによってテスト手法もまちまちです。

  • ロジック
  • 操作
  • 要素、テキスト
  • スタイル
  • ブラウザ間の差異

HTMLの構造や見た目に依存したテストは、正しくテストを書く難易度も高く、しかも変更により壊れてしまう可能性も高いため、得られるメリットがコストに見合わないと感じることもあります。

SPA移行前後の比較

今回一番達成したかった目的は、SPA移行前後のコードに対する検証です。
何かしらのテストの必要は感じていましたが、移行前後のコード全てに対してテストを追加するコストはかけられません。

ビジュアルリグレッションテストは期待される動作の定義と検証を簡単に行うことができます。
期待される動作の定義はスナップショットを撮るだけです。
現在の状態を期待される動作として定義し、移行後の状態と比較することで、移行前後のコードの挙動に変化がない(またはある)ことを確認できます。

ツール

調べてみたところ様々なツールがありました。
PhantomJS は開発が終了しているため、それをベースにしたツールは今回選択肢から外すことにしました。
また、マルチブラウザのテストを考慮すると Selenium ベースが望ましかったのですが、学習コストやフロントエンドエコシステムとの相性の観点から Node.js, Puppeteer を採用している BackstopJS を採用することにしました。

reg-suit

スナップショットの比較とレポートの作成に特化したツールです。
スナップショットの生成をどのように行うかは自由なため、Headless Chrome を使ったり Selenium ベースのツールを使うなど柔軟に対応できそうです。
CIにも組み込みやすそうです。

github.com

Loki

Storybook に特化したツールです。
スナップショットの生成からレポートの作成、結果の承認まで全部入りのツールです。
ページ全体のスナップショットを利用するテストとは違い、コンポーネントの単体テストという位置づけです。
kurashiru でも最近 Storybook が導入されたのでいずれ試してみたいです。

github.com

Wraith

Ruby ベースのツールで、GitHubのスターも一番多いようです。
スナップショットの生成からレポートの作成、結果の承認まで全部入りのツールです。
メインは PhantomJS ベースのようですが Chrome にも対応しているようです。
BBC News が作っています。

github.com

BackstopJS

スナップショットの生成からレポートの作成、結果の承認まで全部入りのツールです。
今回はこのツールを採用しました。
Node.js 製でメインのブラウザに Puppeteer を採用しています。
Puppeteer の API をそのまま利用できるので、テストケースを async / await で書くことができます。
hideSelector や removeSelector で特定の要素を非表示にしたり取り除いたりすることができたりと、いい感じのパラメータが多く用意されています。

github.com

テストのフロー

f:id:delyjp:20180802000550p:plain

GitHub + CodeBuild + BackstopJS

CodeBuild の GitHub 連携の機能を利用し、特定のルールにマッチするブランチ名が push された時にテストが走るように設定しました。
CodeBuild ではテストに使用する Docker イメージを指定することができるのですが、BackstopJS が提供している Docker イメージがあり、これを利用しています。
これで CI 環境 で Puppeteer を動かすための手間はかかりません。
一つだけ注意が必要なのは、そのままだと日本語フォントが入っていないため、フォントの入ったイメージを作るか、install フェイズなどでフォントを入れる必要があります。

version: 0.2

phases:
  install:
    commands:
      - apt-get update -y
      - apt-get install -y apt-transport-https
      - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
      - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
      - apt-get install -y yarn
      - apt-get install -y fonts-ipafont-gothic fonts-ipafont-mincho # 日本語フォントをインストール
      - apt-get install -y python-dev
      - curl "https://bootstrap.pypa.io/get-pip.py" | python
      - pip install awscli

  pre_build:
    commands:
      - REGRESSION_TEST_BRANCH_NAME=$(git branch -a --contains $CODEBUILD_SOURCE_VERSION)
      - mkdir -p ./.yarn-cache
      - yarn install --cache-folder ./.yarn-cache

  build:
    commands:
      - yarn reg:testcase-gen # スプレッドシートから backstop.json を生成
      - backstop reference # リファレンスのスナップショット
      - backstop test # テストのスナップショット

  post_build:
    commands:
      - aws s3 cp --recursive backstop_data/html_report s3://xxxxxxxxxxxxxxxx/$CODEBUILD_BUILD_ID/html_report/
      - aws s3 cp --recursive backstop_data/bitmaps_reference s3://xxxxxxxxxxxxxxxx/$CODEBUILD_BUILD_ID/bitmaps_reference/
      - aws s3 cp --recursive backstop_data/bitmaps_test s3://xxxxxxxxxxxxxxxx/$CODEBUILD_BUILD_ID/bitmaps_test/
      - yarn reg:slack # Slackに通知

cache:
  paths:
    - '.yarn-cache'
    - 'node_modules/**/*'

ステージング環境

Reference と Test には kurashiru の開発で利用しているステージング環境を使用しています。
開発者が任意のブランチをデプロイできるようになっています。
データベースの内容によってスクリーンショットに差が出ないように、ビジュアルリグレッションテスト用のインスタンスは同じデータベースを参照するようにしています。

テストケースは Google スプレッドシートで管理

BackstopJS のテストケースは backstop.json というファイルの scenarios プロパティで設定します。

{
  "scenarios": [
    {
      "label": "recipes_show",
      "onBeforeScript": "src/on_before.js",
      "url": "https://example.com/recipes/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "referenceUrl": "https://example.com/recipes/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "delay": 3000,
      "removeSelectors": [
        ".adsbygoogle",
        ".google-ads"
      ],
      "onReadyScript": "src/on_ready.js",
      "selectorExpansion": true,
      "misMatchThreshold": 0.1,
      "requireSameDimensions": true
    }
  ]
}

もちろんこのまま利用してもよいのですが、backstop.json が巨大になるとメンテナンスが大変そうなので、Google スプレッドシートでテストケースを管理することにしました。

f:id:delyjp:20180802000659p:plain

一行が一つのテストケースに対応しており、上記の JSON の各種パラメータを設定できるようにしています。
また、パタメータとは別に自由に設定できるタグ用のカラムを追加しました。
このタグを利用して、特定のテストケースのみを実行したり、どの画面か、管理サイトかなどで色分けできます。

テスト開始前にこのスプレッドシートのデータを取得し backstop.json を生成します。

各テストケースが見やすく編集もしやすくてとても便利なのですが、テストケースが Git の管理下ではなくなるというデメリットもあります。

結果を S3 にアップロードして Slack に通知

BackstopJS はテスト結果を HTML + アセット群というかたちで生成します。
このようにスクリーンショットの比較結果をとても見やすく表示してくれます。

f:id:alluser:20181207182333p:plain

f:id:alluser:20181207182750p:plain

この HTML + アセット群を静的サイト向けにセットアップした S3 にアップロードし、Slack に URL を通知します。

まとめ

SPAへの移行では実際に多くの意図しない挙動を1px単位で見つけることができました。
このタイミングで導入できてとても良かったです。

ビジュアルリグレッションテストは他のテストを代替するようなものではないですが、技術スタックに依存しないことと、テストケースが壊れにくいというところが大きな利点かと思います。
ビジュアルリグレッションテストを導入して快適なフロントエンドライフを送りましょう!

【追記】

多くの方に読んでいただきとても嬉しいです🙌反応をいただいた点について追記しました!

運用が大変ではないか?

現状、リグレッションテストに関しては、ユニットテストやE2Eテストのように、コミット度にテストを回して、テストケースが全てパスしないとマージできない、という運用はしていません。

雰囲気としては、

「今回だいぶ変更箇所多くなったなー、一応リグレッションテスト見ておくか」
「うわ、差分めっちゃ出てる、こりゃよく分からんね...」
「そうすね、今回はざっと見て問題なさそうだったら、新しいほうをリファレンスにしちゃって下さい」
「了解です!大丈夫そうです!」
「マージ!」

こんな感じです。

テスト実行の有無は都度判断し、もやは有益でない差分については無視しても構わないくらいの運用です。
通常のテストとは違いこのくらいの運用でも十分効果があります。

1pxの違いにそこまで工数かける?

上記のような運用方法が前提であれば、そもそもこの点は解消するかもしれません。

一つの例として1pxの線が挙げられると思います。
デザイン上1pxの線が使われている部分はkurashiruにも多くあります。
1pxのズレで大きなデザイン崩れになることは無いかもしれませんが、1pxの線でも、その線が消えてしまうと大きく意味が変わってしまう、ということはよくあると思います。

その線が誤って消えてしまったりすると、

「すいません!ここの線が消えちゃってました!」
「あっ!すいませんすぐ修正しますー(たぶんあの変更だ...)」

のように、そのページを見て気づいた人からの報告ベースでの修正ということはよくあります。(それ自体は良いと思います🙌)
要は、開発者や関係者が手動で目grepで確かめないといけなかった部分を、ある程度置きかえられるので、ページ数にもよりますが、費用対効果としては悪くないと思います。

広告が差し込まれたり変わっただけでテストが壊れるのでは?

これはその通りなのですが、BackstopJSの機能で対応可能です。
テストケースごとに特定のセレクタにマッチした要素をvisibility: hiddenにしたり、display: noneすることができます。
下のリンク先にあるhideSelectorsremoveSelectorsというオプションです。

github.com

他にも色々な機能があるので、ある程度のケースには対応できると思います。