こんにちは。クロステック・オフィスの benishouga です。

 

この記事は「CRESCO Advent Calendar 2020」20日目の記事です。

今回は PlayWright というライブラリについて、調べる機会があったので、PlayWright とは?という説明を少しと、簡単な使い方として「ユーザー操作を記録してのコード生成」「マルチブラウザ向けの E2E テスト」を行ってみたいと思います。

今回使用した環境は、 Windows 10, Node.js v14.15.0, npm 6.14.8 となっています。

PlayWright とは

PlayWright とは Microsoft が中心になって開発する Node.js 上からブラウザを操作するためのライブラリです。

同じようなソフトウェアとしては Google が開発している Chrome 向けの Puppeteer や 古くから E2E テストで使われる Selenium が有名ですね。 これらのソフトウェアはテストランナーとともに利用することで、E2E テストを行うことができ、Window を表示しない ヘッドレスモード で動かすことができるので、CI などでも利用することができます。

PlayWright の特徴は以下の通りです。

  • 対象ブラウザが Chromium / Firefox / WebKit となっており、現在主流の レンダリングエンジン および JavaScript エンジン を押さえている
    • Firefox と WebKit についてはブラウザ側にパッチを当て、ライブラリから操作するための穴を開けている様子
  • デバイスの Viewport や Geolocation、権限、タッチ操作など、モダンブラウザやモバイルデバイスで使われる機能のエミュレートができる
  • テストを安定させるために setTimeout に依存しない自動化の推奨と、それを推進するための API を提供している
  • 開発は Chrome 向けのブラウザ操作ライブラリの Puppeteer を開発していたメンバーが中心となっていると、過去に Github 上の FAQ で記載されていました。ライブラリの API は、非常に Puppeteer と近いものとなっており、簡潔で使いやすい印象です。

    ユーザー操作を記録してのコード生成

    ユーザー操作の記録には Playwright CLI というツールを使うことでできます。

    Playwright CLI では今回使用するコード生成の他、モバイル端末のエミュレートや、Geolocation やタイムゾーンの偽装、ブラウザの DevTools API をコマンドラインから操作する などができるようです。

    少し前に Puppeteer 向けでは DevTool でユーザー操作を コードで記録する機能 が追加される予定というニュース出ていましたね。

    playwright-cli インストール

    適当なディレクトリで playwright-cli をインストールしていきます。

    $ npm i -D playwright-cli
    > playwright@1.6.2 install C:\work\trial-playwright\node_modules\playwright
    > node install.js
    Downloading chromium v827102 - 89.4 Mb [====================] 100% 0.0s
    chromium v827102 downloaded to C:\Users\xxx\AppData\Local\ms-playwright\chromium-827102
    Downloading firefox v1205 - 74.9 Mb [====================] 100% 0.0s
    firefox v1205 downloaded to C:\Users\xxx\AppData\Local\ms-playwright\firefox-1205
    Downloading webkit v1383 - 51.4 Mb [====================] 100% 0.0s
    webkit v1383 downloaded to C:\Users\xxx\AppData\Local\ms-playwright\webkit-1383
    + playwright-cli@0.162.1
    added 40 packages from 365 contributors and audited 40 packages in 279.223s
    3 packages are looking for funding
    run `npm fund` for details
    found 0 vulnerabilities

    Chromium, Firefox, WebKit のダウンロードが行われるため、結構時間かかります。

    ユーザー操作からコードを生成する

    以下のようなコマンドを入力することでブラウザが起動し、ユーザー操作の記録が行われ、コードが生成されるようになります。

    $ npx playwright-cli codegen https://playwright.dev/

    コード生成はデフォルトで Chromium を使って行われます。また現在 WebKit を使ったコード生成はまだ制限されているようです。

    今回操作としては

    1. PlayWright のページを開く
    2. 検索テキストボックスを選択
    3.  “test” と入力
    4. 検索結果の一番上を選択

    というようなことをやっています。

    以下の、左はブラウザを操作している様子、右はコードが生成されていく様子。
    (画像サイズ削減のために早送りです)

    生成されたコードは以下の通り。

    const { chromium } = require("playwright");
    (async () => {
    const browser = await chromium.launch({
    headless: false,
    });
    const context = await browser.newContext();
    // Open new page
    const page = await context.newPage();
    // Go to https://playwright.dev/
    await page.goto("https://playwright.dev/");
    // Click input[placeholder="start typing to search"]
    await page.click('input[placeholder="start typing to search"]');
    // Fill input[placeholder="start typing to search"]
    await page.fill('input[placeholder="start typing to search"]', "test");
    // Click text=/.*Integrations >.*/
    await page.click("text=/.*Integrations >.*/");
    // assert.equal(page.url(), 'https://playwright.dev/#version=v1.6.2&path=docs/test-runners.md&q=');
    // Click text="Test Runners"
    await page.click('text="Test Runners"');
    // Close page
    await page.close();
    // ---------------------
    await context.close();
    await browser.close();
    })();

    生成されるコードが非常に直感的で読みやすいですね。このコードは Node.js で実行するだけで、ブラウザの起動から~操作~停止まで自動で行われます。

    引き続きこの生成されたコードを利用して E2E テストを作ってみます。

    playwright-test を使用した E2E テスト

    E2E テストには PlayWright を使ったクロスブラウザテストがお手軽に書ける playwright-test を使用します。

    playwright-test のインストール

    $ npm i -D @playwright/test

    いつも通りですね。

    テストコードの作成

    playwright-test は Jest などと同じように、 spec を作成すると、自動で認識され、実行できるようになります。 記述したテストは、対象となるブラウザ Chromium / Firefox / WebKit すべてで実行され、それぞれ個別にテスト結果を出力します。

    以下のような形でテストコードを記述します。

    import { it, expect } from "@playwright/test";
    it("Test name", async ({ page }) => {
    // ここにテストコードを記述
    });

    引数として受け取れる page オブジェクトを通して、画面遷移やブラウザを操作します。 引数には page オブジェクトの他、実際に実行されているブラウザ名なども必要に応じて受け取ることができます。

    先程、操作から生成したコードのうち、 page オブジェクト取得後のコード転記し、タイトル部の検証を追記、 以下のような内容で trial-playwright.spec.ts というファイル名を作成してみました。

    import { it, expect } from "@playwright/test";
    it("Test with playwright", async ({ page }) => {
    // Go to https://playwright.dev/
    await page.goto("https://playwright.dev/");
    // Click //h1[normalize-space(.)='🎭 Playwright']
    await page.click("//h1[normalize-space(.)='🎭 Playwright']");
    // Click input[placeholder="start typing to search"]
    await page.click('input[placeholder="start typing to search"]');
    // Fill input[placeholder="start typing to search"]
    await page.fill('input[placeholder="start typing to search"]', "test");
    // Click text=/.*Integrations >.*/
    await page.click("text=/.*Integrations >.*/");
    // assert.equal(page.url(), 'https://playwright.dev/#version=v1.6.2&path=docs/test-runners.md&q=');
    // Click text="Test Runners"
    await page.click('text="Test Runners"');
    // h1 が "Test Runners" となっているか検証する
    const h1Text = await page.innerText("h1");
    expect(h1Text).toBe("Test Runners");
    });

    実運用では生成されたコードはそのまま使うではなく、無駄な操作を削ったり、 Selector のクエリをより簡潔に修正したりする必要があるかもですが、コードがシンプルなのでそれも簡単に行えそうなこと分かるかと思います。

    テストコードの実行

    テストの実行は以下の感じで folio という @playwright/test に同封されているテストランナーを使って行うことができます。

    $ npx folio

    folio とは @playwright/test のために作成されたテストランナーで、様々な環境や条件を事前に定義し、その定義を使ったテスト実行をサポートするようで、@playwright/test ではこの機能を使い、各々のブラウザに対しテストを実行しているようです。

    実行結果は以下の通りです。

    $ npx folio
    Running 3 tests using 3 workers
    3 passed (7s)

    1 つしかテストを書いていないのに、ブラウザごとに計 3 つのテストが実行されていることが分かります。

    最後に、結果を分かりやすくエビデンスとして確認するために、各ブラウザごとにスクリーンショットを撮るようにしてみます。

    import { it, expect } from "@playwright/test";
    it("Test with playwright", async ({ page, browserName }) => {
    // ... 略 ...
    await page.screenshot({ path: `screenshot/${browserName}.png` });
    });

    引数から browserName を受け取るようにし、その名前を使用し、スクリーンショットを保存するメソッドを叩くだけです。非常に簡単ですね。

    得られたスクリーンショットは以下の通りです。

    それぞれブラウザごとで若干の差はあれど、正しくスクリーンショットが撮れていること確認できました。

    注意点

    自分の手元にある Linux (Mint)マシンで @playwright/test を使用しテストを行おうとした際、 WebKit だけ 「libvpx.so, libicui18n.so, libicuuc.so などの依存ライブラリが見つからない」という旨のエラーが発生し、正常に起動できませんでした。 CI 環境を Linux マシンで構築する場合などは注意が必要そうです。

    まとめ

    今回は PlayWright とその周辺ツールを使ってユーザー操作からテストコードの生成と、その実行をやってみました。

    注意点で挙げたように、まだ安定性のところで枯れきっていない感じはありますが、Puppeteer 由来の使いやすさや、お手軽さは目を見張るものを感じました。

    特に SIer では Selenium がまだ E2E テストの自動化ではスタンダードかと思いますが、すでにその多くの機能を PlayWright は有しているように感じました。最近では Puppeteer が firefox に対応したという話もあり、まだシェアがどうなるかはわかりませんが、 Microsoft が開発している PlayWright も注目していっても良いのではないでしょうか。