AppiumとAndroidエミュレータでスマートフォンのクロスブラウザテスト環境を構築する

| 7 min read
Author: fumihiko-kawano fumihiko-kawanoの画像

はじめに

#

スマートフォン対応の Web アプリの場合は、 エンドユーザがスマホでブラウザを起動してアクセスするのが最も一般的な利用形態となっており、スマホ端末とブラウザの組み合わせのバリエーションを考慮した動作確認が必須となっています。

スマホ端末を利用した自動テストを実現できるソリューションは他にも存在するのですが、AWS Device Farm を利用する事を考慮して、その前段階の投稿として、Appium,WebdriverIO,Node.jsを利用した自動テストの実行について記載いたします。

動作確認時に利用したOSSのバージョン

動作確認時に利用したバージョンは以下の通りです。
node 16.20.1
Appium 2.0.0
WebdriverIO 8.13.1
Android Studio Flamingo 2022.2.1

AWS Device Farm

AWS Device Farm は、広範なデスクトップブラウザと実際のモバイルデバイスでテストすることにより、ウェブアプリとモバイルアプリの品質を向上させるアプリケーションテストサービスです。 テストインフラストラクチャをプロビジョニングおよび管理する必要はありません。

開発環境構築

#

Android Studioのインストール

#

Androidエミュレータが必要ですのでAndroid Studioをインストールします。
Android Studioですので、JDKが事前にインストールされてない場合はインストールとJAVA_HOMEを設定します。

https://developer.android.com/studio からインストーラをダウンロードし、デフォルト選択のままでインストールウイザードを進めてインストールを行います。
デフォルト設定のままインストールすることで仮想デバイスが1つ作成されます。

環境変数のANDROID_HOMEの設定が必要です。
Windowsでは、ANDROID_HOMEは%USERPROFILE%\AppData\Local\Android\Sdkを設定します。

sdkのplatform-toolsをpathに追加する必要があります。
Windowsでは、pathに%USERPROFILE%\AppData\Local\Android\Sdk\platform-toolsを追加します。

Node.js のインストール

#

今回の内容には影響ないのですが、AWS Device Farmを利用する場合は、v18系のNode.jsのnpm-bundleでパッケージ化したときに問題が発生しますので、v16系をインストールしてください。
筆者の環境では16.20.1で動作確認しました。

appium のインストール

#
npm install -g appium
appium plugin install relaxed-caps
appium driver install uiautomator2

WebdriverIOのインストールとテストコードの作成

#

node-sample名でディレクトリを作成し、WebdriverIOをインストールします。

mkdir node-sample
cd node-sample
npm init -y
npm install webdriverio

テストコード

#

node-sample直下にandroid-sample.jsを作成します。

android-sample.jsは以下のようになります。
seleniumのテスト用サイトのweb-form.html をオープンして、色々操作しております。
wdio.remoteに渡すoption 以外はコード内のコメントを見ていただければ理解いただけると思います。

const wdio = require("webdriverio");
const assert = require("assert");

const opts = {
path: '/wd/hub',
port: 4723,
//AWS Device Farmで動作させるときにはcapabilitiesは空オブジェクトを指定
capabilities: {
platformName: "Android",
platformVersion: "14",
deviceName: "sdk_gphone64_x86_64",
automationName: "UiAutomator2",
browserName: "Chrome",
chromeOptions: {
'w3c': false,
},
}
};

async function main () {
//WebdriverIOのクライアントの生成
const client = await wdio.remote(opts);

//seleniumのテスト用サイトのweb-form.htmlをオープン
await client.url("https://www.selenium.dev/selenium/web/web-form.html");

//待ち合わせは不要ですが、ウエイトのサンプル実装
await client.pause(3 * 1000);

// Text input
// id=my-text-idの要素を取得
const textInput = await client.$('#my-text-id');
// id=my-text-idの要素が表示されるまでウエイト(タイムアウト:5秒)
await textInput.waitForDisplayed({timeout: 5 * 1000});

// id=my-text-idの要素に値を入力 'w3c': falseでないとうまく動作しないので、AWS Device Farmでは検証不可能
await textInput.setValue("textInputValue");

// id=my-text-idの要素のvalueを検証
assert.equal(await textInput.getValue(), "textInputValue");

//ワンライナーでもOK
assert.equal(await (await client.$('#my-text-id')).getValue(), "textInputValue" );

const passwordInput = await client.$('input[name="my-password"]');
await passwordInput.setValue("passwordInputValue");
assert.equal(await passwordInput.getValue(), "passwordInputValue")

// Dropdown (select)
const mySelect = await client.$('select[name="my-select"]');
await mySelect.selectByAttribute('value', '1');
assert.equal("1", await mySelect.getValue())

// check box selected
const myCheck1 = await client.$('#my-check-1');
assert.equal(await myCheck1.isSelected(), true );

// check box not selected
const myCheck2 = await client.$('#my-check-2');
assert.equal(await myCheck2.isSelected(),false );

// myCheck2に対してwaitForClickableでクリック可能になるまでウエイト
await myCheck2.waitForClickable({timeout: 5 * 1000});

// click myCheck2
await myCheck2.click();
assert.equal(await myCheck2.isSelected(),true );

const myRadio1 = await client.$('#my-radio-1');
const myRadio2 = await client.$('#my-radio-2');

// radio
assert.equal(await myRadio1.isSelected(), true);
assert.equal(await myRadio2.isSelected(), false);

await client.deleteSession();
}

main();

wdio.remoteに渡すoptionの説明

#

AndroidのplatformVersionに14、deviceNameにsdk_gphone64_x86_64、browserNameにChrome、chromeOptionsのw3cをfalseにする事でlegacy modeを有効化するcapabilitiesを設定しております。

platformVersionとdeviceNameは環境毎に異なる場合がありますので、環境に沿った値を設定する必要があります。

const opts = {
path: '/wd/hub',
port: 4723,
//AWS Device Farmで動作させるときにはcapabilitiesは空オブジェクトを指定
capabilities: {
platformName: "Android",
platformVersion: "14",
deviceName: "sdk_gphone64_x86_64",
automationName: "UiAutomator2",
browserName: "Chrome",
chromeOptions: {
'w3c': false,
}
}
};

deviceNameとplatformVersionを取得するために、Android Studioで仮想デバイスを起動後に以下の手順を実施してください。

1. adb devicesでdeviceIdを取得

実行するコマンド

adb devices

adb devicesの実行結果の例

List of devices attached
emulator-5554 device

2. deviceNameを取得

adb devicesで取得したdeviceIdを指定してshellを起動を起動します。

実行するコマンド

# adb -s ${deviceId} shell でshellを起動
adb -s emulator-5554 shell
# shell内で以下のコマンドを実行 shellから抜ける場合はexitコマンドとなります。
settings get global device_name

settings get global device_nameの実行結果の例

sdk_gphone64_x86_64

3. platformVersionを取得

実行するコマンド

# adb -s ${deviceId} shell でshellを起動
# grep叩けない環境であればadb shell getpropで全て出力して確認可能です。
adb shell getprop | grep "ro.product.build.version"

adb shell getprop | grep "ro.product.build.version"の実行結果の例

platformVersionはro.product.build.version.releasの値に対応します。

[ro.product.build.version.incremental]: [10442412]
[ro.product.build.version.release]: [14]
[ro.product.build.version.release_or_codename]: [14]
[ro.product.build.version.sdk]: [34]

テストコードの実行方法

#

Androidエミュレータから仮想デバイスを起動

#

Android Studioを起動し、表示されるダイアログのMore Actionsをクリックします。
エミュレータ起動画面1

Virtual Device Managerをクリックします。
エミュレータ起動画面2

Pixel_3a_API_extension_level_7_x86_64の実行ボタンをクリックします。
エミュレータ起動画面3

Pixel_3a_API_extension_level_7_x86_64は2023年7月23日時点でAndroid Studioがインストール時にデフォルトで作成する仮想デバイスの名前となります。

appiumサーバの起動

#

-gでグローバルインストールを行っていますので、以下のコマンドを叩くだけで起動します。

appium --base-path /wd/hub --use-plugins=relaxed-caps --allow-insecure chromedriver_autodownload

起動に少し時間がかかりますが、以下のように表示されれば起動完了です。

appium起動完了

テストコードの実行

#

node-sample直下で以下のコマンドを実行すると仮想デバイスでChromeが起動して、実装した処理が実行されます。

node android-sample.js

実行結果の例
テスト実行結果

まとめ

#

AWS Device Farmを利用する前段として、Appium,WebdriverIO,Node.jsを利用した自動テストの実行について説明さていただきました。

今回は、エミュレータで動作する仮想端末を対象に実行したのですが、実機を利用することも可能ですので、興味ある方は実機を接続して実行してみてください。

豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 基本から理解するJWTとJWT認証の仕組み (2022-12-08)
  2. AWS認定資格を12個すべて取得したので勉強したことなどをまとめます (2022-12-12)
  3. Nuxt3入門(第4回) - Nuxtのルーティングを理解する (2022-10-09)
  4. Backstageで開発者ポータルサイトを構築する - 導入編 (2022-04-29)
  5. Nuxt3入門(第8回) - Nuxt3のuseStateでコンポーネント間で状態を共有する (2022-10-28)
  6. Viteベースの高速テスティングフレームワークVitestを使ってみる (2022-12-28)
  7. ORマッパーのTypeORMをTypeScriptで使う (2022-07-27)
  8. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)
  9. GitHub Actions - 構成変数(環境変数)が外部設定できるようになったので用途を整理する (2023-01-16)
  10. Jest再入門 - 関数・モジュールモック編 (2022-07-03)