35
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

モバイル実機 × ブラウザ × 自動テストの構成紹介

ドリコム Advent Calendar 2017

これは ドリコム Advent Calendar 2017 の11日目です。
10日目はhisano1210さんによる、『デザイナーへターミナルのすゝめ』です。

はじめに

自分の所属しているチームではブラウザゲームのフロントエンド基盤開発を行っています。基盤として提供するとなると品質は重要ですよね。
特にモバイル向けとなると実機でのパフォーマンスなどシビアに見る必要があります。
という訳で、パフォーマンスなどの品質を定量的に評価するために【モバイル実機 × ブラウザ × 自動テスト】の環境を構築しました。

業務で構築したものは複雑なテストや複数台の端末を使用するためなかなかヘビーな仕上がりですが、できる限りシンプルでミニマムな構成に整理したうえで、モバイル実機でブラウザ自動テストが行える構成をご紹介します。(使用モジュール等の詳しい説明は割愛します。インストールの項目などで公式サイトへのリンクを貼っているので、そちらをご確認ください。)

環境情報

Mac

ソフトウェア バージョン
macOS 10.12.6 (Sierra)
Homebrew 1.3.8
Node.js 8.4.0
npm 5.4.2
Xcode 9.1
Android SDK Tools 25.2.4
Android SDK Platform-tools 25.0.2

モバイル実機

機種 OS
Galaxy S7 edge Android 7.0
iPhone X iOS 11.1.1

構築手順

前提

  • HomebrewやNode.jsがインストールされている
  • Androidアプリがビルドできる状態である(開発者モードで動かせればOK)
  • Wildcardな開発用のProvisioningProfileでiOSアプリのビルドができる状態である
  • 環境構築するディレクトリでnpm initなどの基本的な設定が終わっている

Appiumのインストール

モバイル用のテスト自動化OSSであるAppiumをnodeモジュールとしてインストール

$ npm install appium --save-dev

Appiumは、ブラウザテストのデファクトスタンダードであるSeleniumと同様のAPIでモバイルテストを行う事ができます。
日本SeleniumユーザーコミュニティでもSeleniumと共に扱われているので、モバイルブラウザのテストを行うならAppiumを使用するのが良いと思います。

Appiumのセットアップ

appiumの構築状況チェックツールであるappium-doctorもインストール

npm install appium-doctor --save-dev

appium-doctorを実行して構築状況を確認

$ node node_modules/appium-doctor/

下図のように全部のチェックが通るまで、指摘内容を確認しつつ設定を進める
appium-doctor.png

Android用の追加セットアップ

特になし:smile:

iOS用の追加セットアップ

iOS WebKit Debug Proxy のセットアップ

PCからモバイルSafariを操作するためのOSSであるiOS WebKit Debug Proxyをインストール

$ brew install ios-webkit-debug-proxy

iOS実機にてSafariのWebインスペクタを有効にする

 設定 > Safari > 詳細 > Webインスペクタ

XCUITest Driverのセットアップ

appiumでXCUITestを使うためのDriverの依存パッケージをインストール

$ brew install libimobiledevice --HEAD
$ brew install ios-deploy

ios-deployのインストールでXcodeのコマンドラインツールが見つからないエラーが出たら下記コマンドでインストール

$ xcode-select --install

後述のテスト実行時にエラーxcodebuild failed with code 65が出る場合はWebDriverAgentのビルドに失敗しています(ここが一番ハマります)。下記コマンドでWebDriverAgentのXcodeプロジェクトを開いてWebDriverAgentLibWebDriverAgentRunnerに対してWildcardなProvisioningProfileなどを設定し、一度手動でビルドしてみてください(詳しくはこちら)

$ open -a xcode ./node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/WebDriverAgent.xcodeproj

WebdriverIOをインストール

Node.js環境でAppiumを操作するためのOSSライブラリであるWebdriverIOをインストール

$ npm install webdriverio --save-dev

WebdriverIOはAppium公式のGetting Startedでも使われています。
また、@cognitomさんが投稿されている[Seleniumアレルギーのための処方箋]でWebDriver(Selenium)クライアントの比較をされていますが、WebdriverIOが良い感じですね。

Mochaをインストール

Node.js環境で動くテストフレームワークであるMochaをインストール

$ npm install mocha --save-dev

MochaはWebdriverIOのテストフレームワークとして公式サポートされています。
他にJasmineなども使えますが、今回は社内で使い慣れたMochaを選択しました。(参考)

構成まとめ

iOS用のmacOSパッケージ類

  • ios-webkit-debug-proxy
  • libimobiledevice
  • ios-deploy

Nodeモジュール類

package.json
{
  "private": true,
  "dependencies": {},
  "devDependencies": {
    "appium": "^1.7.1",
    "appium-doctor": "^1.4.3",
    "mocha": "^4.0.1",
    "webdriverio": "^4.9.11"
  }
}

WebdriverIOの実行

Appiumを起動する

WebdriverIOを実行する前にAppiumを起動する

$ node node_modules/appium/
[Appium] Welcome to Appium v1.7.1
[Appium] Appium REST http interface listener started on 0.0.0.0:4723


ログがこんな感じに出たらhttp://0.0.0.0:4723でAppiumが動いています

止める(Ctrl+C)までターミナルの1画面を占有するので、テストは別タブで実行します

Androidで実行

AndroidのChromeでGoogle検索ページを開いてタイトルを取得するコードの例

example.js
const wdio = require('webdriverio');

const options = {
  port: 4723, // Appiumのポート番号に合わせる
  desiredCapabilities: {
    platformName: 'Android',        // Androidで動かすことを指定
    deviceName: 'Galaxy S7 edge',   // これはなんでも良い
    browserName: 'Chrome',          // ブラウザのテストであることを指定
    automationName: 'UiAutomator2', // 実機用のテストフレームワークを指定
  }
};

wdio.remote(options).init()
.url('https://www.google.co.jp')
.getTitle().then((title) => {
  console.log('Title : ' + title);
})
.end()
.catch((error) =>{
  console.log(error);
});

実機をUSBでMacにつないで実行

$ node example.js
Title : Google
$

タイトルが取得できました

iOSで実行

iOSのSafariでGoogle検索ページを開いてタイトルを取得するコードの例

example.js
const wdio = require('webdriverio');

const options = {
  port: 4723, // Appiumのポート番号に合わせる
  desiredCapabilities: {
    platformName: 'iOS',        // iOSで動かすことを指定
    deviceName: 'iPhone X',     // これはなんでも良い
    browserName: 'Safari',      // ブラウザのテストであることを指定
    udid: '<udid>',             // 実機のUDID(これを指定すると実機で動く)
    automationName: 'XCUITest', // 実機用のテストフレームワークを指定
    xcodeOrgId: '<Team ID>',    // https://developer.apple.com/account/#/membershipのTeam ID
    startIWDP: true,            // ios_webkit_debug_proxyの自動実行を指示
  }
};

wdio.remote(options).init()
.url('https://www.google.co.jp')
.getTitle().then((title) => {
  console.log('Title : ' + title);
})
.end()
.catch((error) =>{
  console.log(error);
});

実機をUSBでMacにつないで実行

$ node example.js
Title : Google
$

Android同様、取得できました

MochaとWebdriverIOを組み合わせる

とりあえず組み合わせる

Android版でWebdriverIOとMochaを組み合わせたテストコード作成

test.js
const wdio = require('webdriverio');
const assert = require('assert');

const options = {
  port: 4723, // Appiumのポート番号に合わせる
  desiredCapabilities: {
    platformName: 'Android',        // Androidで動かすことを指定
    deviceName: 'Galaxy S7 edge',   // これはなんでも良い
    browserName: 'Chrome',          // ブラウザのテストであることを指定
    automationName: 'UiAutomator2', // 実機用のテストフレームワークを指定
  }
};

// wdioのインスタンスを作成
const browser = wdio.remote(options);

describe('テストグループ', function() {

  // デフォルトだと2000msでタイムアウトになってしまうので延長設定
  this.timeout(60000);

  before((done) => {
    // 初期化が完了したらコールバック.
    browser.init().call(done);
  });

  after(function(){
    browser.end();
  });

  it('Google検索ページのタイトル取得', () => {
    return browser
    .url('https://www.google.co.jp')
    .getTitle().then((title) => {
      assert.equal(title, 'Google');
    });
  });

  it('GitHubトップページのタイトル取得', () => {
    return browser
    .url('https://github.com')
    .getTitle().then((title) => {
      assert.equal(title, 'The world\'s leading software development platform · GitHub');
    });
  });
});

mochaを実行する npm script としてtestを追加

package.json
{
  "private": true,
  "scripts": {
    "test": "mocha test.js"
  },
  "dependencies": {},
  "devDependencies": {
    "appium": "^1.7.1",
    "appium-doctor": "^1.4.3",
    "mocha": "^4.0.1",
    "webdriverio": "^4.9.11"
  }
}

npm script 実行

$ npm run test

> @ test /Users/Hoge/...
> mocha test.js

  テストグループ
    ✓ Google検索ページのタイトル取得 (2614ms)
    ✓ GitHubトップページのタイトル取得 (5964ms)

  2 passing (44s)

$

テスト成功です

良い感じに組み合わせる

WebdriverIOのオプション定義を、共通、Android、iOSで分割

conf.base.js
exports.wdio = {
  port: 4723
};
conf.android.js
const base = require('./conf.base')

exports.wdio = Object.assign(base.wdio, {
  desiredCapabilities: {
    platformName: 'Android',        // Androidで動かすことを指定
    deviceName: 'Galaxy S7 edge',   // これはなんでも良い
    browserName: 'Chrome',          // ブラウザのテストであることを指定
    automationName: 'UiAutomator2', // 実機用のテストフレームワークを指定
  }
});
conf.ios.js
const base = require('./conf.base')

exports.wdio = Object.assign(base.wdio, {
  desiredCapabilities: {
    platformName: 'iOS',        // iOSで動かすことを指定
    deviceName: 'iPhone X',     // これはなんでも良い
    browserName: 'Safari',      // ブラウザのテストであることを指定
    udid: '<udid>',             // 実機のUDID(これを指定すると実機で動く)
    automationName: 'XCUITest', // 実機用のテストフレームワークを指定
    xcodeOrgId: '<Team ID>',    // https://developer.apple.com/account/#/membershipのTeam ID
    startIWDP: true,            // ios_webkit_debug_proxyの自動実行を指示
  }
});

良い感じのディレクトリ構造にする

root/
  └─ test/ 
      ├─ conf.base.js 共通定義
      ├─ conf.android.js Android用定義
      ├─ conf.ios.js iOS用定義
      └─ spec/test.js テストコード

テストコードを変更し、渡された環境変数を元に定義ファイルを呼び分けるようにする

// 環境変数で渡された名前から対象のコンフィグをロード
const config = require(`../conf.${process.env.NODE_TEST_DEVICE}`);
// wdioのインスタンスを作成
const browser = wdio.remote(config.wdio);

環境変数を設定しつつmochaを実行するtest:androidtest:iosの npm script を記述

package.json
{
  "private": true,
  "scripts": {
    "test:android": "NODE_TEST_DEVICE=android mocha ./test/spec",
    "test:ios": "NODE_TEST_DEVICE=ios mocha ./test/spec"
  },
  "dependencies": {},
  "devDependencies": {
    "appium": "^1.7.1",
    "appium-doctor": "^1.4.3",
    "mocha": "^4.0.1",
    "webdriverio": "^4.9.11"
  }
}

これで、Androidのテストはこんな感じで実行できます

$ npm run test:android

iOSのテスト実行はこうです

$ npm run test:ios

良い感じにシンプルでミニマムな構成に仕上がったんじゃないかなと思うので、こちらに置いておきます。

さいごに

今回は簡単なテストを動かすところまででしたが、業務ではパフォーマンスの計測やcanvas描画の差分確認などに活用しています。是非みなさんも色々試してみてください!(とりあえずAndroidから始めるとサクッといけますよ:smile:

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
35
Help us understand the problem. What are the problem?