Help us understand the problem. What is going on with this article?

AppiumでAndroidアプリのスクリーンショットをとってみた

Appiumとは

Appium

Appiumはネイティブもしくはハイブリッドなモバイルアプリのためのオープンソーステスト自動化フレームワークです。

私は毎週毎週Androidアプリの最新版のスクリーンショットを手動で取るのが嫌になってAppium導入しました:sweat_smile:

UIテストの自動化もできますが、今回はひとまずスクリーンショット取れるところまで。

環境構築

まずはAppiumを動かせる環境を構築します。
Node.jsをインストールしてからnpmでappiumとwd(Webdriver/Selenium 2クライアント)をインストールするだけです。

以下、公式ページから引用

brew install node      # get node.js
npm install -g appium  # get appium
npm install wd         # get appium client
appium &               # start appium
node your-appium-test.js

これでNode.jsで書いたスクリプトを動かせるのですが、スクリプトを全部自分で書くのはとても面倒です。
UIコンポーネントのパスとか自分の頭で考えたくない。。
ですので、appium-desktopをインストールして自動でスクリプトに落としてもらいましょう。

GitHub - appium/appium-desktop: Appium Server and Inspector in Desktop GUIs for Mac, Windows, and Linux

上記からダウンロードしてインストールします。

appium-desktop

appium-desktopを起動すると以下のような画面が表示されます。

image.png

「Start Server v1.15.1」をクリックしてサーバを起動します。

image.png

右上の虫眼鏡ボタンをクリックします。
すると、以下の画面が表示されます。
「JSON Representation」にエミュレータや端末の情報をJSON形式で設定します。

image.png

編集ボタンをクリックして、JSONを記載します。
JSONの記載方法は以下のサイトに詳しく解説があります。

Desired Capabilities - Appium

最低限必要な項目を設定します。
今回はAndroidの設定を行います。

例)
例えばこんな感じです。値は適宜変更してください。

{
    "platformName": "Android",
    "platformVersion": "9",
    "deviceName": "emulator-5554",
    "app": "/Users/miyatay/Documents/flutter_app/build/app/outputs/apk/debug/app-debug.apk"
}
key value
platformName Android
platformVersion 9
deviceName emulator-5554
app [apkの絶対パス]

platformNameはAndroidの場合は"Android"固定
platformVersionはAndroidのOSのバージョンを設定します

image.png

deviceNameはadb devicesコマンドで表示されたデバイス名を設定します

$ adb devices
List of devices attached
emulator-5554   device

以下のキャプチャは上記の設定をした状態です。
image.png

「Start Session」ボタンをクリックしてセッションを開始します。

設定が正しいければ以下の画面が表示されます。
このアプリはAndroidStudioでFlutterプロジェクトを作成すると自動で生成されるカウントアップアプリです。
image.png

もし、画面が真っ白な場合はリロードボタンをクリックしてみてください。
ボタンをクリックしてみます。
画面上部の目のマークのボタンをクリックしてください。
image.png

画面左側にカーソルを合わせると、選択できるコンポーネントが黄色く表示されます。
image.png

TapやSend Keysなどを選択すると動作がキャプチャされます。
画面上部にスクリプトが自動生成されるのでこちらをコピーすると同じ動作を何度でもさせることが可能になります。
Show/Hide Boilerplate Codeを選択することで初期処理をコードに含めるか否か選ぶことができます。
Showすると、ドライバーの初期化や接続の設定情報などを吐き出してくれるのでとても便利です。
image.png

Boilerplate CodeをShowにしてコードをテキストエディタにコピーします。
今回はJS-WD(Promise)を選択しました。

こんな感じになりました。

// Requires the admc/wd client library
// (npm install wd)
// Then paste this into a .js file and run with Node 7.6+

const wd = require('wd');
const driver = wd.promiseChainRemote("http://localhost:4723/wd/hub");
const caps = {"platformName":"Android","platformVersion":"9","deviceName":"emulator-5554","app":"/Users/miyatay/Documents/flutter_app/build/app/outputs/apk/debug/app-debug.apk"};

async function main () {
  await driver.init(caps);
  let el1 = await driver.elementByXPath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.Button");
  await el1.click();
  await driver.quit();
}

main().catch(console.log);

実行するとこうなりました。

$ node sample.js
{ Error: [elementByXPath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.Button")] Error response status: 7, , NoSuchElement - An element could not be located on the page using the given search parameters. Selenium error: An element could not be located on the page using the given search parameters.
    at exports.newError (/Users/miyatay/node_modules/wd/lib/utils.js:152:13)
    at /Users/miyatay/node_modules/wd/lib/callbacks.js:94:19
    at /Users/miyatay/node_modules/wd/lib/webdriver.js:196:5
    at Request._callback (/Users/miyatay/node_modules/wd/lib/http-utils.js:89:7)
    at Request.self.callback (/Users/miyatay/node_modules/request/request.js:185:22)
    at Request.emit (events.js:197:13)
    at Request.<anonymous> (/Users/miyatay/node_modules/request/request.js:1161:10)
    at Request.emit (events.js:197:13)
    at IncomingMessage.<anonymous> (/Users/miyatay/node_modules/request/request.js:1083:12)
    at Object.onceWrapper (events.js:285:13)
  message:
   '[elementByXPath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.Button")] Error response status: 7, , NoSuchElement - An element could not be located on the page using the given search parameters. Selenium error: An element could not be located on the page using the given search parameters.',
  status: 7,
  cause:
   { status: 7,
     value:
      { message:
         'An element could not be located on the page using the given search parameters.' },
     sessionId: '43f216e2-8b0b-481e-baae-63ae74b73c29' },
  inspect: [Function],
  'jsonwire-error':
   { status: 7,
     summary: 'NoSuchElement',
     detail:
      'An element could not be located on the page using the given search parameters.' } }

ボタンのelementが見つけられなくてエラーになっているようです。
elementが見つかるまでの待機時間を設定してみます。
Implicit Wait - Appium

UIAutomatorを使うとNoSuchElementが出にくくなるとの記事も見た気がしますが今回はこのまま突き進みます。。

sample.js
// Requires the admc/wd client library
// (npm install wd)
// Then paste this into a .js file and run with Node 7.6+

const wd = require('wd');
const driver = wd.promiseChainRemote("http://localhost:4723/wd/hub");
const caps = { "platformName": "Android", "platformVersion": "9", "deviceName": "emulator-5554", "app": "/Users/miyatay/Documents/flutter_app/build/app/outputs/apk/debug/app-debug.apk" };

async function main() {
    await driver.init(caps);
    await driver.setImplicitWaitTimeout(5000); // 暗黙的な待機時間を設定
    let el1 = await driver.elementByXPath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.Button");
    await el1.click();
    await driver.quit();
}

main().catch(console.log);

コピペで5秒に設定しました。
再度、node sample.jsでスクリプトを起動します。
一瞬アプリが起動して終了すると思います。
これでスクリプトを動かせるようになりました。

今回はスクリーンショットを取得したいので、以下のページを参考にスクリーンショットを取得してみます。
Screenshot - Appium

sample.js
// Requires the admc/wd client library
// (npm install wd)
// Then paste this into a .js file and run with Node 7.6+

const wd = require('wd');
const fs = require('fs')
const driver = wd.promiseChainRemote("http://localhost:4723/wd/hub");
const caps = { "platformName": "Android", "platformVersion": "9", "deviceName": "emulator-5554", "app": "/Users/miyatay/Documents/flutter_app/build/app/outputs/apk/debug/app-debug.apk" };

async function main() {
    await driver.init(caps);
    await driver.setImplicitWaitTimeout(5000);
    let el1 = await driver.elementByXPath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.View/android.view.View/android.view.View/android.view.View/android.widget.Button");

    // スクリーンショット取得
    let base64 = await driver.takeScreenshot();
    const buffer = Buffer.from(base64, 'base64');
    fs.writeFileSync("screen.jpg", buffer)

    await el1.click();
    await driver.quit();
}

main().catch(console.log);

これで実行すると、screen.jpgが保存されます。
あとは全画面でスクリーンショットを取るスクリプトを書くだけ:rolling_eyes:
モックサーバとの連携も必要ですね。。

以上、Appiumでスクリーンショットを取る方法でした。

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
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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