Aqutras Members' Blog

株式会社アキュトラスのメンバーが、技術情報などを楽しく書いています。

cordovaアプリをCircleCI上でe2eテストする

こんにちは.cordovaデビューした maxmellon です

今回は,cordova で作られたアプリケーションを CircleCI 上 でe2eテストする方法について触れたいと思います

はじめに

この記事の関連ワード

上記の内容を把握していると, より記事の内容がわかりやすいと思います.

e2eラッパにー nightmare をつかう

github.com

end to end テストは,nightmare を使わなくてもそのまま phantomjs と アサーションライブラリを用いれば書くことができるが,なかなかに記述量が多くなりめんどくさい.

また,ドライバーに phantomjs を使うと,Symbol などのモダンなAPIが利用できない.

そこで,nightmare を使う.

nightmare は version2 から,ドライバーが ※1 electron になり,OSなどの環境の違いを意識することなく,e2eテストを書くための環境を提供してくれる.

※1 Electronとは、Node.jsとChromiumをベースとしたデスクトップアプリケーションを作成するクロスプラットフォーム実行環境です。

人々はもう phantomjs のバージョンに悩まなくて済むのだ (2.0.0 以前は ※2 bind() がないため React が動かない,2.0.0 < 2.1.0 は ※3 ファイルアップロードがうまく動かない.など)

※2 Circleci上でPhantomjsを使ってReactアプリをテストする
※3 Webpage.uploadFile not working in phantomjs 2.0

electron であればこういったことに悩まなくて済む. ついでに,selenium diriver みたいに重くもないし起動もそこそこ速いというメリットも得られる.

テストの書きやすさ比較

これは,公式ページにある例が非常にわかりやすいので,引用して載せておく.

f:id:maxmellon:20160721151847p:plain

http://www.nightmarejs.org/より

cordova アプリケーションを nightmare で e2e テストする

テストの書き方

テストは次のように書くことができます.

var Nightmare = require('nightmare');
var nightmare = Nightmare({ show: true })

nightmare
  .goto('http://yahoo.com')
  .type('form[action*="/search"] [name=p]', 'github nightmare')
  .click('form[action*="/search"] [type=submit]')
  .wait('#main')
  .evaluate(function () {
    return document.querySelector('#main .searchCenterMiddle li a').href
  })
  .end()
  .then(function (result) {
    console.log(result)
  })
  .catch(function (error) {
    console.error('Search failed:', error);
  });

これをavaを使って書くと,

import test from 'ava';
import Nightmare from 'nightmare';

const nightmare = Nightmare({ show: true });

test.serial('Generators!', function * (t) {
  const result = yield nightmare
    .goto('http://yahoo.com')
    .type('form[action*="/search"] [name=p]', 'github nightmare')
    .click('form[action*="/search"] [type=submit]')
    .wait('#main')
    .evaluate(() => document.querySelector('#main .searchCenterMiddle li a').href);

  t.true(result.includes('images.search.yahoo.com'));
});

test.serial('Async/Await!', async t => {
  const result = await nightmare
    .goto('http://yahoo.com')
    .type('form[action*="/search"] [name=p]', 'github nightmare')
    .click('form[action*="/search"] [type=submit]')
    .wait('#main')
    .evaluate(() => document.querySelector('#main .searchCenterMiddle li a').href);

  t.true(result.includes('images.search.yahoo.com'));
});

このように書くことができます.

Generator, async/await を使うことにより,非同期でエミュレートされているものの終了を待っています.

詳しいテストを書くためのAPIは,https://github.com/segmentio/nightmare#api を参照してください.

大体は, evaluate で 実DOM を 返して,そのDOMの状態が期待した状態かをチェックしていく感じです.

テストを実行するための環境を構築する

cordova は グローバルにインストールされているとして話を進めていきます.

  • node >= 6.2.0
  • npm >= 3.9.5
$ npm install --save-dev ava nightmare

テストを実行するためのスクリプトを作成する

$ mkdir e2e
$ touch e2e/test.js
# 任意のエディタで e2e/test.js を開く
import test from 'ava';
import Nightmare from 'nightmare';

const nightmare = Nightmare({
  // CI上ではない時は,dev tool と window を表示
  show: process.env.CI !== 'true',
  openDevTools: { detach: process.env.CI !== 'true' },
});

// これより下に,テストケースを追加していく
// Example
test.serial('Generators!', function * (t) {
  const result = yield nightmare
    .goto('http://yahoo.com')
    .type('form[action*="/search"] [name=p]', 'github nightmare')
    .click('form[action*="/search"] [type=submit]')
    .wait('#main')
    .evaluate(() => document.querySelector('#main .searchCenterMiddle li a').href);

  t.true(result.includes('images.search.yahoo.com'));
});

package.json を 次のように編集

  "scripts": {
    "build:browser": "cordova build browser",
    "browser": "npm run build:browser && cordova run browser",
    "pree2e": "npm run browser &",
    "e2e": "wait-on http://localhost:8000 && ava --verbose e2e/test.js",
  },

これで,テストを実行する環境は整いました.

npm run e2e をコマンドラインで実行することでテストが走ります.

CircleCI上で動作させる

CricleCI上で動かすために,特別にすることがありません.

ただ,background で 走らせたサーバーを落とさないかぎり CI も終わらないので,サーバーを落とすタスクを追加します

cirlce.yml を次のように編集します

machine:
  node:
    version: 6.2.0
dependencies:
  override:
    - "npm install"
    - "npm install -g cordova"
  post:
    - "cordova platform add browser"
test:
  override:
    - "npm run e2e"
    - "kill -9 `ps | grep -v grep | grep cordova/run | awk -F ' ' '{print }'` >/dev/null 2>&1 || exit 0"

テスト実行前にplatformを追加しています. テストが終わったあとに,サーバーのプロセスをkillしています.

こうすることで,nightmareによる e2e テストをCircleCI上で実行することができます.

余談

CircleCI では, Xvfd (仮想 X デーモン) がデフォルトで起動しています. なので,特に設定しなくても electron を実行することができました.

TravisCIの場合は,Xvfd をインストールして,起動して DISPLAY環境変数を export することで, 同様に,electron を実行することができます.

詳しくは,こちらを御覧ください

https://github.com/electron/electron/blob/master/docs/tutorial/testing-on-headless-ci.md

まとめ

  • Phantomjs から 脱却することで高速に e2e テストすることができる.
  • CircleCI 上でも e2e テストを実行することができる.
  • electron なので, 開発者のPCのOSに依存することがない.