TWBMT

技術的な記事や覚書について書いていきます。その内、自作サイトとかに技術記事をまとめたい。

【覚書】お試しでスクショ自動化+ diff 比較(簡易VRT)をやってみた

目的

  • ビジュアルリグレッションテストに興味があった。
  • お試しでスクショ自動化と diff 比較をやってみたので振り返りと覚書をまとめる。

やったこと

  1. pupetter でローカルホストにアクセスしてスクショを撮る。
  2. lookSame で取ったスクショを比較する。
  3. 出来栄えを見て喜ぶ。

使用したライブラリ

puppeteer

Google 製のヘッドレス Chrome を操作するライブラリ。
類似ツールの Cypress 等との違いは、Google 製且つCDP (Chrome DevTools Protocol) 準拠の為、Chromeとの親和性が高いことが特徴。

github.com

looks-same

パット見使いやすそうだった為、採用。
github.com

わかったこと

  • 一度経験すればサクッと作れる様になりそう。
    • 実装量は少ない。かけた時間のほとんどが記法の調査だった。
    • もしも知識ゼロで作る場合、ハマりどころは「スクショしたい画面の描画を待つためにスリープ処理が必要」ぐらい。
    • 何か簡単なツールを作りたい時の心理的な抵抗感って数時間で済むような学習コストで減るんだなぁと体感。素振り大事。
  • もしも実運用する時に考慮することは以下の事が懸念事項かも。
    • スクショデータの管理と正解データの更新方法。
    • 差分検知をどれだけ厳密にするか。何故か puppettier のスクショにピクセル単位のズレが出る時があり、厳密にしすぎると落ち易すぎる印象。
    • どこまで画面を操作するか。操作数が増える = テストが壊れるリスクの増加 or メンテコストの増加。

ハマったこと

  • スクショ結果が不安定 同じ画面を同じ状態でスクショしていたにも関わらず、若干の差分やズレが発生した。 おそらく自身の端末の性能(最近、メモリの圧迫によって動作不安定。。)に起因していると思われる。

さじ加減が難しいが、looks-same が許容する差分の閾値を調整してOKということにして対応した。

次にやりたいこと

実装メモ・サンプルコード

コードのイメージを記載しました。 思ったよりも実装量が少ないことが伝われば良いなと。

トグルにまとめました。

スクショ自動化

実装したコードの一部。
puppeteer を起動して、ログインして、画面遷移してスクショするだけ。
全体で約100行ぐらいで、そんなに時間がかからなかった。

const main = async () => {
  const { page, browser } = await initPuppeteer();

  await login(page, {
    loginUrl: LOGIN_URL,
    username: USER_NAME,
    password: PASSWORD,
  });
  await screenShotPages(page);
  await browser.close();
};

const screenShotPages: (page: puppeteer.Page) => Promise<void> = async (
  page: puppeteer.Page
) => {
  for (const path of PATHS) {
    const url = `http://localhost:4200/${path.path}`;
    await page.goto(url);
    await sleep(12_000);
    await screenShot(page);
  }
};

diff 作成

実装したコードの一部。全体では大体120~150行ぐらい。
ファイルパスを渡すだけで比較・比較結果の画像を作成してくれる。
ファイル移動やディレクトリの存在チェックの方がよっぽど面倒だった。

// reference と current にファイルパスを渡すだけで比較してくれる。
function checkDiff(
  reference: string,
  current: string
): Promise<LooksSameResult> {
  return new Promise((resolve, reject) => {
    looksSame(
      reference,
      current,
      {
        tolerance,
      },
      (error, result) => {
        resolve(result);
      }
    );
  });
}