モバイルアプリの自動テストツールShiratesを使ってみよう

本記事は自動テストツールShirates（シラテス）の紹介記事です。※はてなブログからの転載です。


# モバイルアプリのテストの自動化

昨今、ソフトウェアテストの自動化がますます重要になっています。モバイルアプリも例外ではありません。自動化を実現するツールには有償・無償のものが多数存在します。無償のもので注目されている製品としてはAppiumがあります。筆者もAppiumを導入して使用しましたが、とてもハードルが高いという感想です。はっきり言って、茨の道です。ちゃんと使ってテストの実務で成果を出せるようになるにはある程度の期間修行が必要です。開発スキルを持ったエンジニアでも苦労するのですから、もっと導入や使用方法が簡単でないとテストエンジニアに広く普及させるのは難しいと考えています。


# Shiratesとは

Shiratesはそのような敷居の高さを少し下げてくれるOSSのツールです。GitHubから入手できます。

https://ldi-github.github.io/shirates-core/index_ja.html



ざっくりした概要はこちらの記事を参照ください。

https://wave-diary.hatenablog.com/entry/2022/10/08/180457

ざっくりしたデモ動画はこちらをご覧ください。
※2022/10/18追記

https://youtu.be/kwCL11BU2SQ


Shiratesには主に以下のような特徴があります。

* テストコードを記述するための**ほぼクロスプラットフォームで使いやすいAPI**。AndroidとiOSのプラットフォームをサポート
* **強力なログ出力機能とレポーティング機能**を提供
* テスト環境をセットアップするための**柔軟な構成管理フレームワーク**を提供


 

# まずは環境構築

ShiratesはmacOSまたはWindowsで利用できます。

[クイックスタート](https://ldi-github.github.io/shirates-core/quick-start_ja.html) を実施して環境を構築します。


# プロジェクトの作成

プロジェクトを作成します。
※完成したソースコード一式は[こちら](https://github.com/wave1008/shirates-samples-practice1)から入手できます。


1. IntelliJ IDEAを起動し、`File > New > Project` を選択します

1. New Projectウィンドウで`New Project`タプを選択します

1. 以下の項目を入力します
    *  `Name`：（任意の名前）
    *  `Location`：（保存先）
    * `Language`：Kotlin
    * `Build system`：Gradle
    * `JDK`：（適切なバージョンを選択）
    * `Gradle DSL`：Kotlin
    * `Add sample code`：OFF
<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/b1c7d6e0-0530-ecd0-ea24-9ad22f6e3d00.png)


1. `Create` をクリックします。
1. バックグラウンドタスクが完了するまでしばらく待ちます

<br>

**build.gradle.kts**の初期状態は以下のようになります

```kotlin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.7.10"
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test"))
}

tasks.test {
    useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}
```

<br>
必要な記述を追加します。編集後は以下のようになります。

```kotlin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.7.10"
}

group = "org.example"
version = "1.0-SNAPSHOT"

val shiratesCoreVersion = "1.0.1"
val appiumClientVersion = "8.1.0"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test"))

    // JUnit 5
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0")

    // Appium
    testImplementation("io.appium:java-client:$appiumClientVersion")

    // shirates-core
    testImplementation("io.github.ldi-github:shirates-core:$shiratesCoreVersion")

    // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core
    testImplementation("org.apache.logging.log4j:log4j-core:2.18.0")

    // https://mvnrepository.com/artifact/org.slf4j/slf4j-nop
    testImplementation("org.slf4j:slf4j-nop:1.7.36")
}

tasks.test {
    useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}
```

<br>

クリックしてGradleの再読み込みを行います。必要なソフトウェアがダウンロードされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/43a1827c-fb91-9461-94d6-db724405dda1.png)



# テスト構成ファイルの作成

## フォルダとファイルの作成

* プロジェクトディレクトリの直下に`testConfig`フォルダを作成します
* `testConfig`フォルダの直下に`screens`フォルダを作成します
* `screens`フォルダの直下に`[Calculator Main Screen].json`を作成します
* `testConfig`フォルダの直下に`settingsConfig.json`を作成します
* `testConfig`フォルダの直下に`testrun.properties`を作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/9b78af78-3052-610f-5557-4ffde15ad409.png)

## [Calculator Main Screen].json

画面の定義ファイルです。
以下の内容を記述します。

```json
{
  "key": "[Calculator Main Screen]",

  "identity": "[AC][()]",

  "selectors": {
    "[formula]": "#formula",
    "[result final]": "#result_final",
    "[result preview]": "#result_preview",

    "[√]": "#op_sqrt",
    "[π]": "#const_pi",
    "[^]": "#op_pow",
    "[!]": "#op_fact",

    "[AC]": "#clr",
    "[()]": "#parens",
    "[%]": "#op_pct",

    "[÷]": "#op_div",
    "[×]": "#op_mul",
    "[-]": "#op_sub",
    "[+]": "#op_add",
    "[=]": "#eq",
    "[⌫]": "#del",

    "[0]": "#digit_0",
    "[1]": "#digit_1",
    "[2]": "#digit_2",
    "[3]": "#digit_3",
    "[4]": "#digit_4",
    "[5]": "#digit_5",
    "[6]": "#digit_6",
    "[7]": "#digit_7",
    "[8]": "#digit_8",
    "[9]": "#digit_9",
    "[.]": "#dec_point"
  }

}
```


## settingsConfig.json

Appiumによるアプリの起動に関するパラメーターを設定します。
以下の内容を記述します。
```json
{
  "testConfigName": "settingsConfig",

  "appIconName": "電卓",
  "packageOrBundleId": "com.google.android.calculator",

  "startupPackageOrBundleId": "com.android.settings",
  "startupActivity": "com.android.settings.Settings",

  "profiles": [
    {
      "profileName": "Android 12",
      "capabilities": {
        "automationName": "UiAutomator2",
        "platformName": "Android",
        "platformVersion": "12",
        "language": "ja",
        "locale": "JP"
      }
    }
  ]
}
```

## testrun.properties

使用するテスト構成ファイルとプロファイルを設定します。
以下の内容を記述します。
```jproperties
android.configFile=testConfig/settingsConfig.json
android.profile=Android 12
```

# テストコードの作成

## 作成するテストコードの概要

Androidで以下の操作を行い結果を検証するテストコードを作成します。

1. 電卓アイコンをタップして電卓アプリを起動する
1. `[AC]`を打鍵する
1. `123+456=` を打鍵する
1. 計算結果が`579`であることを検証する


## テストコードの作成

* `src/test/kotlin` の直下に`CalculatorTest1`クラスを作成します
* 以下の内容を記述します

```kotlin
import org.junit.jupiter.api.Test
import shirates.core.driver.commandextension.select
import shirates.core.driver.commandextension.tap
import shirates.core.driver.commandextension.tapAppIcon
import shirates.core.driver.commandextension.textIs
import shirates.core.testcode.UITest

class CalculatorTest1: UITest() {

    @Test
    fun test1() {

        scenario {
            case(1){
                condition {
                    it.tapAppIcon("電卓")
                        .tap("[AC]")
                }.action {
                    it.tap("[1]")
                        .tap("[2]")
                        .tap("[3]")
                        .tap("[+]")
                        .tap("[4]")
                        .tap("[5]")
                        .tap("[6]")
                        .tap("[=]")
                }.expectation {
                    it.select("[result final]")
                        .textIs("579")
                }
            }
        }
    }
}
```

# テストコードの実行

* Android 12エミュレーターを起動しておきます。電卓アプリがインストールされていない場合はGoogle Playストアでインストールしてください
* test1()を右クリックし、`Debug`を選択してテストを実行します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/8f0d21a4-7f1c-cb16-9a7c-e03993a67e62.png)

* テストの実行が完了したらコンソール出力の `file://...`のリンクをクリックします。Finder（またはExplorer）が開きます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/04a44bf5-4756-ea75-75ff-508ad523f5da.png)

* `_Report(simple).html`をダブルクリックします。HTMLでレポートが表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/d9ac0104-30dc-e88e-4efb-f0c1b8c9ccce.png)

* `CalculatorTest1@a.xlsx`をダブルクリックします。スプレッドシートでテスト結果を確認できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/4dda7f34-4977-1a95-df56-a2f812b2b94b.png)


# テストコードの解説

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258506/c4ed5442-3921-c555-551b-91985640ef9e.png)

* 8: UITestクラスを継承します
* 10: JUnit 5のTestアノテーションを適用します。これによりtest1関数がテストメソッドとしてJUnit 5に認識されます
* 13: scenario関数を実行します
* 14: case関数を実行します。引数でテストケース番号を指定します
* 15: condition関数を実行します。ここには事前条件を記述します
* 18-26: action関数を実行します。電卓のキーをタップします
* 27-29: expectation関数を実行します。実行結果を検証します


このように[Shirates](https://github.com/ldi-github/shirates-core/blob/main/doc/markdown/index.md)では簡素な記述でテストを書くことができます。