ChromiumベースのアプリケーションでCodeceptJSを使ったE2Eテストをする方法


Puppeteer Connectを使う

Puppeteer Connectを使うと、Remote Debugging Portを通して、Devtools Protocolを用いたブラウザの操作ができます。

そこにCodeceptJSを組み合わせて、E2Eテストをします。

ElectronなどのChromiumベースのフレームワークで、CodeceptJSが要求するChromiumのAPIが用意されていれば使えます。

今回は、C#向けのChromiumバインディングである、CefSharpを用いて説明していきます。

Remote Debugging Portの設定

CefSharpについてあまり詳しくは書きませんが、ちょっと試してみたい場合はこちらのリポジトリを試してみてください。

まずは、Remote Debugging Portを設定します。

var settings = new CefSettings()
{
    RemoteDebuggingPort = 9222
};

起動すると、

にアクセスできます。

右がChrome Devtoolsの画面で、左がCefSharpで起動したアプリです。

右のChrome DevtoolsをRemote Debugging Port経由で操作します。

ブラウザの自動操作

puppeteerを使って、既存のChromiumインスタンスに接続します。

にアクセスすると、接続のための情報が取れます。

{
   "Browser": "Chrome/75.0.3770.100",
   "Protocol-Version": "1.3",
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
   "V8-Version": "7.5.288.23",
   "WebKit-Version": "537.36 (@cd0b15c8b6a4e70c44e27f35c37a4029bad3e3b0)",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/e463d684-e9c0-4c81-91e3-b29a685d238d"
}

使うのは、webSocketDebuggerUrlのみです。

このエンドポイントを通して、puppeteerで接続します。

npm install --save-dev puppeteer request

requestモジュールは、先ほど書いたJSONを取ってくるのに使います。

const puppeteer = require("puppeteer");

const request = require("request");

async function main() {
  const { webSocketDebuggerUrl } = await new Promise((resolve, reject) => {
    request("http://localhost:9222/json/version", function(err, res, body) {
      if (err) {
        reject(err);
      } else {
        resolve(JSON.parse(body));
      }
    });
  });

  puppeteer
    .connect({
      browserWSEndpoint: webSocketDebuggerUrl
    })
    .then(async browser => {
      const pages = await browser.pages();

      const page = pages[0];

      await page.evaluate(() => {
        const input = document.querySelector("input[title=検索]");

        input.value = "hello world";
      });

      browser.disconnect();
    });
}

main();

前半は、requestモジュールで接続情報を取得していて、

後半で、puppeteer.connectを用いてChromiumインスタンスに接続し、Google検索欄に'hello world'と入れています。

右がVSCodeで、Node.jsのスクリプトを実行すると、左側のアプリケーションで入力欄に「'hello world'」と入ったことがわかります。

なんか、ブラウザのウィンドウサイズがおかしくなってスクロールが出ていますが、

気になる場合はpuppeteer.connect()のオプションのdefaultViewPortで幅と高さを好みのものに指定しましょう。

  puppeteer
    .connect({
      browserWSEndpoint: webSocketDebuggerUrl,
      defaultViewport: {
        width: 1024,
        height: 700
      }
    })

これで、ブラウザの自動操作ができました。

CodeceptJSを使いE2Eテストをする

最後に、CodeceptJS + Puppeteerです。

npm install --save-dev codeceptjs

codeceptjsをセットアップします

npx codeceptjs init

テストドライバーはもちろんpuppeteerを選んでください。

他はお好みで。。。

CodeceptJSを使って、Cefsharpブラウザを操作するには、WebSocketのエンドポイントを指定する必要があります。

下記のように、codecept.conf.jsで、Helperに指定します。

codecept.conf.js
exports.config = {
  helpers: {
    Puppeteer: {
      url: "http://localhost",
      chrome: {
        browserWSEndpoint:
          "ws://localhost:9222/devtools/browser/c5aa6160-b5bc-4d53-bb49-6ecb36cd2e0a"
      }
    }
  },
  ...
};

今度は、http://localhost:9222/json/versionに同期的にアクセスし、WebSocketのエンドポイントを取得しましょう。

npm install sync-request --save-dev

sync-requestは、同期的にHTTPリクエストができるモジュールです。

このように書き直します。

codecept.conf.js
const request = require("sync-request");
const res = request("GET", "http://localhost:9222/json/version");

const { webSocketDebuggerUrl } = JSON.parse(res.getBody());

exports.config = {
  tests: "./*_test.js",
  output: "./output",
  helpers: {
    Puppeteer: {
      url: "http://localhost",
      chrome: {
        browserWSEndpoint: webSocketDebuggerUrl
      }
    }
  },
  include: {
    I: "./steps_file.js"
  },
  bootstrap: null,
  mocha: {},
  name: "cefsharp-puppeteer-connect",
  translation: "ja-JP"
};

これでOKです。

あとはテストを書くだけです。

下記のコマンドでテストを生成できます。

npx codeceptjs gt

生成されたテストを下記のように変更します。

Feature("search input test");

Scenario("type hello world", I => {
  I.fillField("input[title=検索]", "hello world");
});

すっきりしすぎて笑えました。

あとはテストを実行するだけです。

npx codeceptjs run --steps

しっかり入力欄にタイプされてますね。

これでCefsharpでE2Eテストができました。

終わりに

Chromiumベースであれば、Puppeteerから接続できるので、後は同じです。

Electronでもできると思います。

Cefsharpは、.NET系だと一番メジャーだと勝手に思っているんですが、今までE2Eテストに関する資料を見つけられずにいましたが、

色々漁っていた時に、こちらの記事を見つけて、希望を見出しました。

GoogleのWeb ToolingによるDeveloper支援の充実さを感じました。

Special Thanks

最後に、1つだけ紹介させてください。

今回初めて、Qiita記事にGIFアニメーションを導入しました。

べたべた画像を張り付けるよりも記事の可読性が上がったと思います。

GIFの作成に、ScreenToGifがめちゃくちゃ役に立ちました。

画面をキャプチャし、簡単にGIFが作れます。

是非、お試し下さい。