スクショをベースにするスナップショットテスト
目的
CSSやJSコードをいじった後、見た目の差分を確認できます。
Reactの場合、いっぱい使われている共通のコンポーネント修正した後、既存のコンポーネントへの影響を確認することができます。
必要なパッケージ
- jest https://facebook.github.io/jest/
- puppeteer https://github.com/GoogleChrome/puppeteer
- jest-image-snapshot https://github.com/americanexpress/jest-image-snapshot
テストのカバーするページの定義
/routes/Test.tsx
interface RouteData { // ページ名 name: string; // ページpath path: string; // ページ表示用のテストContainer Component component: React.SFC<{}> | React.ComponentClass<RouteComponentProps<any>>; // 横画面のスクショ比較が必要かどうか isHorizontalTestNeeded?: boolean; } // テスト実行時用データ export const testRouteDatas: RouteData[] = [ { name: "enzaメンバー登録ページ", path: "/register", component: UserSimpleRegisterPage, isHorizontalTestNeeded: true, }, { name: "エントランスページ", path: "/entrance", component: EntrancePage, isHorizontalTestNeeded: true, }, { name: "ゲームポータルトップページ", path: "/game", component: TopPage, }, // ... ]; // テスト用のpathを作る export const generateTestPath = (path: string) => ( `/snapshotTest${path}` ); // Route情報データ export const testRoute = testRouteDatas.map((data) => ( <Route path={generateTestPath(data.path)} key={generateTestPath(data.path)} component={data.component} exact={true} /> ));
全体Route定義
App.tsx
import { testRoute } from "routes/Test"; // ... <Switch location={location}> {/* develop環境だけテストのRouteにアクセスできる */} {ENV === "develop" ? testRoute : null} </Switch> // ...
スナップショットテスト定義
test/snapshot.ts
import * as puppeteer from "puppeteer"; import { generateTestPath, testRouteDatas } from "routes/Test"; import * as QueryString from "query-string"; let browser; // Headless Chrome Browser を立ち上げ beforeAll(async () => { browser = await puppeteer.launch({ args: ["--no-sandbox", "--disable-setuid-sandbox"] }); }); testRouteDatas.forEach(async (routeData) => { const testUrl = `http://develop.env.host.name/${ generateTestPath(routeData.path) }`; it(routeData.name, async () => { const page = await browser.newPage(); page.setViewport({ width: 360, height: 640, }); await page.goto(testUrl); // 縦画面のページスクショ const image = await page.screenshot({ fullPage: true }); // 現存のスクショと比較 expect(image).toMatchImageSnapshot(); }); if (routeData.isHorizontalTestNeeded) { it(`横画面-${routeData.name}`, async () => { const page = await browser.newPage(); page.setViewport({ width: 640, height: 360, }); await page.goto(testUrl); // 縦画面のページスクショ const image = await page.screenshot({ fullPage: true }); // 現存のスクショと比較 expect(image).toMatchImageSnapshot(); }); } }); // Headless Chrome Browser を閉じる afterAll(async () => { await browser.close(); });
jestでtoMatchImageSnapshot
メソッドを使えるように
jest-setup.ts
import { toMatchImageSnapshot } from "jest-image-snapshot"; expect.extend({ toMatchImageSnapshot });
コマンド追加
package.json
{ "scripts": { "test:snapshot": "jest /test/snapshot.ts", "test:snapshot:update": "jest /test/snapshot.ts --updateSnapshot" } }
使い方
yarn test:snapshot
でテストを実行します。
差分が検出されたら、下記のようなファイルが作成されます。
差分問題なかったら、下記のコマンドでスナップショットを更新します。
yarn test:snapshot:update
Todo
実はpuppeteer
でクリックやキーボード入力などイベントを発火できます。
動作を定義できたらもっと前面にテストすることが可能だと思います。