SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

reg-suitとCypressを使ってビジュアルリグレッションテストを導入した話

こんにちは!スマートキャンプでインサイドセールスに特化した SaaSを作っているエンジニアの井上です。

携わっている上記のプロダクトは使いやすさにこだわっているため、UIの修正を行うことが多々あります。 そんななかで起こった問題の対応としてビジュアルリグレッションテストを導入したので、今回はその話をできればと思います。

背景

ビジュアルリグレッションテストを導入する前に、UIに大きく影響する改修がありました。

影響範囲が多岐に渡る改修だったので、全画面をテストする必要がありましたが、画面ごとの表示崩れが無いかを確認する作業がとても苦行で、かつ時間もかかってしまいました。 また、その後もボタンが表示されていないなどの問題が発生してしまったことから、以下を検討しました。

  • UIテストの自動化によるテスト工数削減
  • 意図しないUI変更の検知をする仕組みの導入

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

コンポーネントやページのスクリーンショットを、事前に用意した正解の画像と比較することで、ピクセルレベルでの差分を検出するテスト手法のことです。

ビジュアルリグレッションテストをやる目的

目的としては、人力によるチェックの負担を軽減するところにあります。 今回の背景にもあったUIテストの工数削減と意図しないUI変更の検知です。

特に、修正した内容をエンジニアが意識しなくても自動でテストしてもらうことで工数の削減をしたいなと思いました。 今回はその手段としてビジュアルリグレッションテストを実施することに決めました。

何をテストをするのか?

変更が多く、かつ使われてることも多い箇所をテスト対象にしていくことが重要だと思っています。 しかし、いきなり全てのテストをやるのは工数がかかりすぎるので短期でできて効果が高いものをスタートとして、まずはページ単位での表示崩れを検知できることを目的にしました。

使用したツール

Cypress

Cypressはブラウザでのテストを自動化するテストフレームワークです。

立ち上がると、テストコードを更新する度にホットリロードがかかりテストが実行されることが個人的に嬉しいです。

もともとE2Eテストの文脈で導入していましたが、スクリーンショット機能がある、かつ今回の対象がページ単位のテストのためCypressで画像の取得を行うことにしました。

reg-suit

画像群を比較し、レポート作成するまでの責務を持ちます。 Cypress内でビジュアルリグレッションテストまで行ってしまうことは可能ですが、"画像の取得"と"比較"の役割を分けられるというメリットがあります。

なので、今回作った画像比較の仕組みは、画像取得の部分さえ変えれば比較ロジックはそのままreg-suitを使用できます。

また、reg-suitにはプラグインを通してSlackやgithubとの連携も可能です。 今回使ったプラグインは以下のものになります。

  • reg-simple-keygen-plugin
    • スナップショットキーとして任意の文字列を指定できる
  • reg-publish-s3-plugin
    • レポートをS3にアップロードする
  • reg-notify-slack-plugin
    • レポートをslack通知する

画像を取得する

ビジュアルリグレッションテストをするにあたり、比較元になる正解の画像(EXPECTED)とUI修正後の画像(ACTUAL)の2つが必要です。

前述した通り、今回はCypressのスクリーンショット機能を使って取得しますが、正解画像とUI修正画像はreg-suit側で判断するため、Cypressはシンプルに画像を取得するのみになります。

/// <reference types="cypress" />

context('Actions', () => {
  beforeEach(() => {
    cy.visit('http://localhost:3000')
  })

  it('top screenshot', () => {
    cy.screenshot("test")
  });
})

画像の差分を検知する

今回作成したreg-suitによる画像の差分検知では、以下のようなフローで差分を検知します。

  1. フォルダ名がEXPECTED_KEYの正解画像をS3から取得
  2. Cypressで取得した画像とS3から取得した画像を比較
  3. 比較結果をACTUAL_KEYをフォルダ名としてアップロード
  4. Slackに比較結果を通知

実際使用した設定は以下のようなものです

  {
    "core": {
      "workingDir": "reg",
      "actualDir": "cypress/screenshots",
      "threshold": 0,
      "ximgdiff": {
        "invocationType": "client"
      }
    },
    "plugins": {
      "reg-simple-keygen-plugin": {
        "expectedKey": "${EXPECTED_KEY}",  
        "actualKey": "${ACTUAL_KEY}"
      },
      "reg-publish-s3-plugin": {
        "bucketName": "${BUCKET_NAME}"
      }
      ,
      "reg-notify-slack-plugin": {
        "webhookUrl": "${WEBHOOK_URL}"
      }
    }
  }

Circle CIによる自動化

画像の検知の仕組みをCircle CIのフローにのせていきます。 CIでやることとしては2つで、ベース画像の更新とページごとのビジュアルテストです。

ベース画像の自動更新

ベース画像の更新はエンジニアが意識せずとも最新の状態になるようにしたいと考えたため、Gitのmasterブランチにマージされたタイミングでベースの画像更新をしています。

ビジュアルリグレッションテストの実行

ビジュアルリグレッションテストの流れとしては

  1. テスト環境へCypressが画像を取得(Actual Image)
  2. reg-suitがS3に保存している正解の画像(Expected Image)を取得
  3. reg-suitで画像の差分テスト
  4. reg-suitのテスト結果をS3に保存

最終的な Cypress と reg-suit のテストフロー図

ハマったところ

  • デフォルトの状態ではCircle CIに日本語フォントがないため、ページの日本語が豆腐文字(□□□)になっていました。

    • Circle CIに日本語フォントをインストールすることで解決しました。
  • E2Eなどでのあるあるですが、画像の取得が安定せず苦戦しました。

    • 特定の秒数waitさせる力技により安定させました。

結果どうなったか

ピクセル単位で差分を検知できる状態になり、masterマージ時にベース画像が更新されるのでエンジニアが意識せずともリソースを最新の状態に保てる形になりました。

reg-suitの結果画面

今後の改善・取り組みたいこと

今回はページごとのテストでしたが、変更の多いUIのすべてをテストできる形にし、より予期せぬ変更を検知できる形にできればなと思います。