Appium + Android + Node.js で 画面要素の取得・操作とテスト結果の評価をおこなう

2021.06.17

いわさです。

前回は以下の記事で Appium の動作環境を準備し確認を行いました。

今回は AWS Device Farm でこのAPKファイルを自動実行しようと思い、DeviceFarmでAPKファイルのアップロードを試みました。

しかし...

There was a problem processing your file. We found that your application requires device admin permissions. Currently we do not allow apps which require device admin permissions. Please verify the permissions required by running the command "aapt dump xmltree AndroidManifest.xml", and make sure that output does not contain the keyword "android.permission.BIND_DEVICE_ADMIN". For more information about this issue, please see the documentation.

Device Farm の前提条件や制限については別途記事にしようと思いますが、パーミッションで BIND_DEVICE_ADMIN が許可されているアプリは、Device Farm では使えないようです。

そこで本日は、Device Farms での自動実行を前に、テスト対象アプリとテストコードを作成してみます。
なお、本記事では Appiumクライアント は Node.js にて作成します。

テストアプリの作成

Android StudioでHelloWorld的な簡易アプリを作成します。
EditTextが2つあり、数値を入力しボタンを押すと、足した数値を表示するというよくあるやつです。

コードを次のようになっており、エラー処理など一切入れていません。
レイアウトXMLも最低限です。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/txtParam1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <EditText
            android:id="@+id/txtParam2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/btnInput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="click" />
        <TextView
            android:id="@+id/lblOutput"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.tak1wa.hellohello

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var txt1: EditText = findViewById(R.id.txtParam1)
        var txt2: EditText = findViewById(R.id.txtParam2)
        val btn: Button = findViewById(R.id.btnInput)
        val lbl: TextView = findViewById(R.id.lblOutput)

        btn.setOnClickListener {
            lbl.setText(
                (txt1.text.toString().toInt() + txt2.text.toString().toInt()).toString()
            )
        }
    }
}

テストコードの作成

テストコードの下準備は 前回の記事 を参考にします。

Androidアプリを操作する、値を取得する部分などは WebDriverIOモジュールを使って実装します。
テスト部分はNode.jsのAssertモジュールを使って実装します。

自動操作の内容は次のようなものにします。
1つ目のEditTextに 2 を、2つ目のEditTextに 3 を入力し、ボタンを押します。
表示された計算結果を取得し、それが 4 であることを確認します。

index.js

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

const opts = {
  path: '/wd/hub',
  port: 4723,
  capabilities: {
    platformName: "Android",
    platformVersion: "11",
    deviceName: "Android Emulator",
    app: "/Users/iwasa.takahito/Downloads/app-debug.apk",
    appPackage: "com.tak1wa.hellohello",
    appActivity: ".MainActivity",
    automationName: "UiAutomator2"
  }
};

async function main () {
  const client = await wdio.remote(opts);
  const txt1 = await client.$(await client.findElement("id", "com.tak1wa.hellohello:id/txtParam1"));
  await txt1.setValue("2");

  const txt2 = await client.$(await client.findElement("id", "com.tak1wa.hellohello:id/txtParam2"));
  await txt2.setValue("3");

  const btn = await client.$(await client.findElement("id", "com.tak1wa.hellohello:id/btnInput"));
  await btn.click();

  const lbl = await client.$(await client.findElement("id", "com.tak1wa.hellohello:id/lblOutput"));
  const value = await lbl.getText();
  assert.strictEqual(value,"4");

  await client.deleteSession();
}

main();

実行

index.js を実行すると、自動操作が行われます。

期待どおり、AssertionErrorが表示されました。

(node:33892) UnhandledPromiseRejectionWarning: AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:

'5' !== '4'

テストコードの期待値を 5 にするとエラーがなく自動テストが終了します。

assert.strictEqual(value,"5");

まとめ

簡単なアプリとテストコードを作成してみました。
Androidの画面要素を取得、操作するあたりはWeb上の情報が少なくて、少し苦労しました。

以下のように、ライブラリの説明をよく読んで、実装するのが一番簡単だと思います。