はじめに
こんにちは、ラクマの@itinaoです。
E2Eテストについて、概要からお手軽に試す方法までを全5編で記載しています。
- E2Eテスト: 導入の必要性・何を導入するのか
- E2Eテスト: TestCafeを試す
- E2Eテスト: Github Actions上でTestCafeを試す(PCブラウザ編)← 今回はココ
- E2Eテスト: Github Actions上でTestCafeを試す(モバイルブラウザ編)
- E2Eテスト: Selenium Gridを試す
前回の記事で、TestCafeをローカルの環境で実施する手法を記しました。
ローカルで実行できるだけでも便利ですが、 任意のタイミングで自動実行できるようになると更に便利です。
今回はお手軽に実施する方法として、Github Actionsを使った方法を説明していきます。
Github Actionsとは
Github Actionsとは、GitHub上で動作するサーバレス実行環境のことです。
Actionの実体はDockerコンテナで、 ユーザはGitHubのインフラ上で任意のDockerイメージからコンテナを起動し、その中で任意のコマンドやスクリプトを実行できます。
リポジトリのデータがコンテナ内にマウントされた状態になるので、コマンドやスクリプトからソースコードに直接アクセス可能です。
詳細はこちらをご参照ください。
https://docs.github.com/ja/actions
Github Actions上で動作するOS
実行コストやEmulator等についても記載していますが、これらのサーバーが扱えます。
実行コストについての詳細は、こちらをご参照ください。 macosでたくさん実行するのは要注意です。
どのAndroid Emulator / iPhone Simulatorが使えるかの詳細は、こちらをご参照ください。
https://github.com/actions/virtual-environments#available-environments
Github Actions上でTestCafeを実行する
今回はこのようなOSの区分けで、E2Eテストを実行していきたいと思います。
最終的なディレクトリ構成はこの様になります
※ package.jsonや、yarn run dev
で動く対象以外
$ tree e2e/ .github/ e2e/ ├── run_mobile_chrome.sh ├── run_mobile_safari.sh ├── run_safari.sh ├── runner.js └── src └── index └── index.ts .github/ └── workflows └── test-e2e.yml
実行する内容
- Github Actions上でWEBサーバーを起動する
- Github Actions上で起動しているWEBサービスに対してE2Eテストを行う
もちろん、 PublicなWEBサービスについてもアクセスできるので、その場合はWEBサーバー起動部分はないものと補完して頂ければと思います。
Windows: Microsoft Edge / Google Chrome / Firefox
下記は失敗する例です。。
.github/workflows/test-e2e.yml を追加します。
name: E2E Test on: [push] jobs: install-apps: name: install strategy: matrix: os: [windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Cache node modules uses: actions/cache@v2 env: cache-name: cache-node-modules with: path: '**/node_modules' key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }} - name: yarn install # TestCafeもここでインストールしているものとする run: yarn install --frozen-lockfile test-windows: name: Run tests across latest browsers on windows strategy: matrix: browser: ['edge', 'chrome', 'firefox'] runs-on: windows-latest timeout-minutes: 20 needs: [install-apps] steps: - uses: actions/checkout@v2 - name: Cache node modules uses: actions/cache@v2 env: cache-name: cache-node-modules with: path: '**/node_modules' key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }} - name: Run TestCafe run: | yarn run dev > /dev/null 2>&1 & # サーバー起動&バックグラウンドへ yarn run testcafe ${{ matrix.browser }} # テスト開始 - uses: actions/upload-artifact@v2 if: always() with: name: reports-windows-${{ matrix.browser }} path: reports/
これと同じ書き方をMac上で実行すると成功するのですが、Windowsではうまくいきませんでした。
下記の部分に curl
を挟むと分かりやすいのですが、
Macの場合はサーバーが起動するまで curl
は待機しますが、
Windowsの場合は curl: (7) Failed to connect to localhost port 3035: Connection refused
となって即座にエラーとなります。
- name: Run TestCafe run: | yarn run dev > /dev/null 2>&1 & # サーバー起動&バックグラウンドへ curl http://localhost:3035 yarn run testcafe ${{ matrix.browser }} # テスト開始
ということで、起動するまで待つようにしました。
Windowsのパワーシェルでスクリプトを書く力量がなかったので、Node.jsを用いてます。
test-windows: name: Run tests across latest browsers on windows strategy: matrix: browser: ['edge', 'chrome', 'firefox'] runs-on: windows-latest timeout-minutes: 20 needs: [install-apps] steps: - uses: actions/checkout@v2 - name: Cache node modules uses: actions/cache@v2 env: cache-name: cache-node-modules with: path: '**/node_modules' key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }} - name: Run TestCafe run: yarn run e2e env: BROWSER: ${{ matrix.browser }} - uses: actions/upload-artifact@v2 if: always() with: name: reports-windows-${{ matrix.browser }} path: reports/
package.json に下記の記述を追加します。
{ "scripts": { "e2e": "node e2e/runner.js", ...
e2e/runner.js に下記の記述を追加します。
このような流れです。 1. サーバー起動 2. HTTPリクエストが成功するまで待つ 3. テスト実行
const child_process = require('child_process') const consola = require('consola') const crossSpawn = require('cross-spawn') const http = require("http") const spawn = (command) => { const [c, ...args] = command.split(' ') return crossSpawn(c, args) } const serve = () => { const served = spawn('yarn run dev') served.stdout.on('data', (data) => { consola.log(`${data}`) }) served.stderr.on('data', (data) => { consola.error(`${data}`) }) served.on('close', (code) => { consola.info(`${code}`) }) return served } const runE2E = (served) => { const command = `yarn run testcafe ${process.env.BROWSER}` const e2e = spawn(command) e2e.stdout.on('data', (data) => { consola.log(`${data}`) }) e2e.stderr.on('data', (data) => { consola.error(`${data}`) }) e2e.on('close', (code) => { served.kill() process.exit(code) }) } const checkConnection = () => { return new Promise((resolve, reject) => { const helthCheckURL = 'http://localhost:3035' const requestTimeout = 1000 const req = http .get(helthCheckURL, resolve) .on('error', reject); req.setTimeout(requestTimeout) req.on('timeout', () => { req.abort() }) }) } const main = () => { const run = async (served) => { try { await checkConnection() runE2E(served) } catch (_error) { const retryInterval = 1000 setTimeout(run.bind(this, served), retryInterval) } } run(serve()) } main()
これをGithub Actions上で実行すると、安定して成功します。
Mac: Safari
Mac上のSafariの実行も一工夫が必要になります。
公式にも普通にやったらGithub Actions上では動かないよ。 という記載があり、解決策もセットで書かれています。
Github Actions use the macOS Catalina 10.15 virtual environment with "System Integrity Protection" enabled as macos-latest. With this setting enabled, TestCafe requires screen recording permission, which cannot be obtained programmatically. For this reason, TestCafe is unable to run tests with GitHub Actions locally on macos-latest.
公式のやり方を参考に、 e2e/run_safari.sh というファイルを作成します。
export HOSTNAME=localhost export PORT1=1337 export PORT2=1338 yarn run testcafe remote --hostname ${HOSTNAME} --ports ${PORT1},${PORT2} & pid=$! sleep 10 open -a Safari http://${HOSTNAME}:${PORT1}/browser/connect wait $pid
そして、それを実行させるために e2e/runner.js にも修正を加えます。
... const runE2E = (served) => { let command switch (`${process.env.BROWSER}`) { case 'safari': command = `./e2e/run_safari.sh` break; default: command = `yarn run testcafe ${process.env.BROWSER}` } const e2e = spawn(command) e2e.stdout.on('data', (data) => { consola.log(`${data}`) }) e2e.stderr.on('data', (data) => { consola.error(`${data}`) }) e2e.on('close', (code) => { served.kill() process.exit(code) }) } ...
.github/workflows/test-e2e.yml はこのようになります。 windowsと記載のある部分をmacosに修正したくらいの変更です。
name: E2E Test on: [push] jobs: install-apps: name: install strategy: matrix: os: [macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Cache node modules uses: actions/cache@v2 env: cache-name: cache-node-modules with: path: '**/node_modules' key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }} - name: yarn install run: yarn install --frozen-lockfile test-macos: name: Run tests safari on macos strategy: matrix: browser: ['safari'] runs-on: macos-latest timeout-minutes: 20 needs: [install-apps] steps: - uses: actions/checkout@v2 - name: Cache node modules uses: actions/cache@v2 env: cache-name: cache-node-modules with: path: '**/node_modules' key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }} - name: Run TestCafe run: yarn run e2e env: BROWSER: ${{ matrix.browser }} - uses: actions/upload-artifact@v2 if: always() with: name: reports-macos-${{ matrix.browser }} path: reports/
とすると、このように成功します。
ただし、たまに下記のようなエラーが発生するので、調査が必要そうです。
ERROR NativeBinaryHasFailedError: The find-window process failed with the 1 exit code.
おわりに
お手軽に、、といいつつ、あんまりお手軽ではなかったです。
モバイルに関しては、次の記事で解説していきます。