LoginSignup
6
4

More than 1 year has passed since last update.

CircleCi・Github ActionsそれぞれでAndroidアプリのE2Eテストを自動化する

Last updated at Posted at 2021-08-12

はじめに

1から簡単なAndroidアプリを作成し、それに対するE2EテストをAppiumで自動化しCI環境で実行できるようにする所までをやってみたのでその備忘録を残しておく。

※本記事はwebdriverioを用いているが、あえて最小で構築しているので@wdio/cliは利用していない。

構築したアプリ・e2e自動テストのコード・CIパイプラインのコードは以下のGitHubにある。

内容

セクションは大きく3つで、

に分かれている。

Androidアプリを作成する

アプリの実装

Androidアプリのdeveloper向けのサイトにある公式チュートリアルがステップバイステップで丁寧&日本語字幕・解説があるので、これでアプリを作成するのがおススメ。

Kotlin での Android の基礎
※見た目は日本語モードでも英語になっているが、コンテンツの中身・字幕は日本語がある

※上記のKotlin での Android の基礎は、
 https://developer.android.com/training/basics/firstapp?hl=ja#where-to-go-from-here から飛べるリンクのページ

AVD(エミュレータ)の作成

アプリを実際にPC上で起動しその動きを確認するためにはAVD(エミュレータ)が必要になるので仮想デバイスを作成して管理するを参照して作成する。

※ちなみに、このAVDはAppiumでE2Eテストを実行する時にも必要になる。

AVD(エミュレータ)の起動方法

Android Studioから起動するには以下の図の〇で作成したAVD名を選択し、▶をクリックすればいい。
image.png

ただ、このやり方だとAVDの起動時にイチイチAndroid Studioを立ち上げる必要があり面倒。
そこでemulator -list-avdsコマンドで作成済みのAVD一覧を表示させ、そのemulator {AVD名}コマンドを実行するとAndroid Studioから毎回起動しなくてもAVDを起動できる。

C:\Users\user>emulator -list-avds
Pixel_3_XL_API_28

C:\Users\user>emulator @Pixel_3_XL_API_28

E2Eテストの実装対象になるアプリ一覧

実際に作成したアプリ① Happy Birthday Card App

単にメッセージが表示されているだけのアプリで、ソースコード全体はここ
image.png

AppiumによるE2Eテスト

モバイルアプリのE2Eテスト自動化で定番のAppiumを使ってE2Eテストを実装した。
E2Eテストを実装する上でやった事としては以下。

  • ローカル環境構築(CIについてはCIの項を参照)
  • webdriverioの導入・テスト実装
  • mochaの導入
  • ロケータ指定する部分はutilityに
  • webdriverioのlogをconsole上に出力させない

実際にE2Eテストを実装する上でどのようにロケータ情報などを調べるのか?については以下の項を参照。

  • debbugerでのロケータ取得方法

ローカル環境構築

appium-doctorでAndroidやiOSのアプリのテストのためのAppiumを実行する環境があるかを確認できる。
私の環境では以下のような結果だったのでJAVA_HOME・ANDROID_HOME(Java SDK, Android SDKのパス)の設定を行ったのでその時のlogを以下に書き残す(Windows)。

> appium-doctor

info AppiumDoctor Appium Doctor v.1.16.0
info AppiumDoctor ### Diagnostic for necessary dependencies starting ###
info AppiumDoctor  ✔ The Node.js binary was found at: C:\Program Files\nodejs\node.EXE
info AppiumDoctor  ✔ Node version is 14.16.0
WARN AppiumDoctor  ✖ ANDROID_HOME environment variable is NOT set!
WARN AppiumDoctor  ✖ JAVA_HOME environment variable is NOT set!
WARN AppiumDoctor  ✖ adb, android, emulator could not be found because ANDROID_HOME or ANDROID_SDK_ROOT is NOT set!
WARN AppiumDoctor  ✖ Cannot check %JAVA_HOME% requirements since the environment variable itself is not set
info AppiumDoctor ### Diagnostic for necessary dependencies completed, 4 fixes needed. ###

JAVA_HOMEの設定

https://adoptopenjdk.net/ から openjdk11 をインストールする。
※インストールの際、カスタムセットアップの画面でSet JAVA_HOME variableがデフォルトだと× インストールしないになっているので、ローカルハードドライブにインストールに変更しないとパスが通らない。

インストール完了後、javac --versionとコマンドプロンプトで実行しバージョンが表示されれば OK。
(バージョンが表示されない時は再起動してJAVA_HOMEのパスがwindowsに通るようにする。)

ANDROID_HOMEの設定

Androidのアプリを作成するためにAndroid Studioをインストールすると、必要なAndroid SDK(+ SDK Tools)はインストール済みなはずなので、あとはそのSDKがある場所をWindowsの環境変数に設定してパスを通せばいい。
ただ、環境変数の登録にあたっては順番が大事なので注意。

私の環境では、C:\Users\user\AppData\Local\Android\Sdkに SDK があったのでそれをそれぞれ、

  • ANDROID_HOME
    C:\Users\user\AppData\Local\Android\Sdk
  • Pathに以下の順でそれぞれ追加
    • %ANDROID_HOME%emulator
    • %ANDROID_HOME%tools
    • %ANDROID_HOME%platform-tools

を環境変数に追加した。

最終的に再度appium-doctorを実行して以下のようにnecessary dependenciesが全て✔になっていればOK。

info AppiumDoctor Appium Doctor v.1.16.0
info AppiumDoctor ### Diagnostic for necessary dependencies starting ###
info AppiumDoctor  ✔ The Node.js binary was found at: C:\Program Files\nodejs\node.EXE
info AppiumDoctor  ✔ Node version is 14.16.0
info AppiumDoctor  ✔ ANDROID_HOME is set to: C:\Users\user\AppData\Local\Android\Sdk
info AppiumDoctor  ✔ JAVA_HOME is set to: C:\Program Files\AdoptOpenJDK\jdk-11.0.11.9-hotspot\
info AppiumDoctor    Checking adb, android, emulator
info AppiumDoctor      'adb' is in C:\Users\user\AppData\Local\Android\Sdk\platform-tools\adb.exe
info AppiumDoctor      'android' is in C:\Users\user\AppData\Local\Android\Sdk\tools\android.bat
info AppiumDoctor      'emulator' is in C:\Users\user\AppData\Local\Android\Sdk\emulator\emulator.exe
info AppiumDoctor  ✔ adb, android, emulator exist: C:\Users\user\AppData\Local\Android\Sdk
info AppiumDoctor  ✔ 'bin' subfolder exists under 'C:\Program Files\AdoptOpenJDK\jdk-11.0.11.9-hotspot\'
info AppiumDoctor ### Diagnostic for necessary dependencies completed, no fix needed. ###

webdriverioの導入・テスト実装

今回はappiumでの自動化なので、appium serverとの通信を行う事になのでセッションを作る必要があるが、AppiumのGetting Startedに紹介されているやり方に沿って以下のように実装する。
(以下は単にセッションを繋いで切るだけのソースコード。remote()で作成したセッションに対してhttps://webdriver.io/docs/api/webdriver に書かれているメソッドを使って実際にアプリを操作する。)

capabilitiesについてはここを参照。

const wdio = require("webdriverio");

const opts = {
    path: '/wd/hub',
    port: 4723,
    capabilities: {
        platformName: "Android",
        platformVersion: "9", // ここはandroidのemulatorのversion
        deviceName: "Android Emulator",
        app: `${__dirname.replace("__e2e__", "")}app/build/outputs/apk/debug/app-debug.apk`,  // Androidのアプリ本体
        automationName: "UiAutomator2"
    }
};

async function main() {
    const client = await wdio.remote(opts);
    await client.deleteSession();
}

main();

mochaの導入

単純に以下のようにwebdriverioで操作をしてexpect.jsでassertionをしてもlog上はappium clientのlogが出るだけ結果が分かりにくいのでmochaを導入した。

index.js
// 省略
async function main() {
  const client = await wdio.remote(opts);

  const field = await client.$('//*[@resource-id="com.example.happybirthday:id/textView"]');
  const value = await field.getText();
  expect(value).to.equal("Happy Birthday, Sam!");

  await client.deleteSession();
}

main();
node index.js

mochaの導入にあたって見たページは以下。

mochaで書いたソースコードは以下。

const wdio = require("webdriverio");
const expect = require("expect.js");
const by = require("../wedio/locator-helper");
const opts = require("../wedio/config");

describe('Create session and delete session', () => {
    let client;

    before(async () => {
        client = await wdio.remote(opts);
    });

    describe('Happy Birthday App Test', () => {
        it('check title text', async () => {
            const field = await client.$("//android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[1]/android.view.ViewGroup/android.widget.TextView");
            const value = await field.getText();
            expect(value).to.equal("Happy Birthday");
        });

        it('check message text', async () => {
            const field = await client.$(by.resourceId("com.example.happybirthday:id/textView"));
            const value = await field.getText();
            expect(value).to.equal("Happy Birthday, Sam!");
        });

        it('check from text', async () => {
            const field = await client.$(by.resourceId("com.example.happybirthday:id/textView2"));
            const value = await field.getText();
            expect(value).to.equal("From Emma.");
        });
    })

    after(async () => {
        await client.deleteSession();
    });

})

const by = require("../wedio/locator-helper");const opts = require("../wedio/config");のソースコードはGitHubから確認できる。

ロケータ指定する部分はutilityに

webとかであればfindElement()などのメソッドをわざわざ呼び出さずともselectorsに書かれているような方法で簡単に要素を探し出せるが、Androidなどのモバイルだとそうはいかず、何通りかの方法で要素をしているする事になる。
※Android独自の要素指定方法であるUiSelector を用いて要素を探す時はAndroid UiAutomatorに書かれている方法が使える。

  • resource-id
  • xpath
  • UiSelector

こういったものをソースコード上に書いてもいいがutilityとして別に切り出して用意しておくと便利。
具体的には以下のような感じ。

module.exports = {
    /**
     * リソースIDを指定するセレクタ
     * @param {string} id resourceId-id
     * @returns xpath
     */
    resourceId(id) {
        return `//*[@resource-id="${id}"]`;
    },

    /**
     * クラス名を指定するセレクタ
     * @param {string} className 
     * @returns UiSelector
     */
    className(className) {
        return `android=new UiSelector().className("${className}")`;
    }
}

webdriverioのlogをconsole上に出力させない

何も設定をしないと以下のようにappium clientのlogとmochaのlogがごっちゃになって何が何だか分からなくなる。

> mocha --timeout 30s

  Happy Birthday App Test
2021-07-30T07:32:27.849Z INFO webdriver: Initiate new session using the WebDriver protocol
2021-07-30T07:32:27.981Z INFO webdriver: [POST] http://127.0.0.1:4723/wd/hub/session
2021-07-30T07:32:27.982Z INFO webdriver: DATA {  省略
2021-07-30T07:32:40.013Z INFO webdriver: COMMAND findElement("xpath", "//android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[1]/android.view.ViewGroup/android.widget.TextView")
2021-07-30T07:32:40.015Z INFO webdriver: [POST] http://127.0.0.1:4723/wd/hub/session/27d30517-4d1e-4128-bb9e-733c5dd774d3/element
2021-07-30T07:32:40.015Z INFO webdriver: DATA {
  using: 'xpath',
  value: '//android.widget.FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[1]/android.view.ViewGroup/android.widget.TextView'
}
2021-07-30T07:32:40.255Z INFO webdriver: RESULT {
  'element-6066-11e4-a52e-4f735466cecf': 'f65a9d48-fff2-40b1-8301-5f11fa307990',
  ELEMENT: 'f65a9d48-fff2-40b1-8301-5f11fa307990'
}
2021-07-30T07:32:40.269Z INFO webdriver: COMMAND getElementText("f65a9d48-fff2-40b1-8301-5f11fa307990")
2021-07-30T07:32:40.269Z INFO webdriver: [GET] http://127.0.0.1:4723/wd/hub/session/27d30517-4d1e-4128-bb9e-733c5dd774d3/element/f65a9d48-fff2-40b1-8301-5f11fa307990/text
2021-07-30T07:32:40.303Z INFO webdriver: RESULT Happy Birthday
    ✔ check title text (294ms)
2021-07-30T07:32:40.308Z INFO webdriver: COMMAND findElement("xpath", "//*[@resource-  省略
2021-07-30T07:32:40.309Z INFO webdriver: [POST] http://127.0.0.1:4723/wd/hub/session/27d30517-4d1e-4128-bb9e-733c5dd774d3/element
2021-07-30T07:32:40.309Z INFO webdriver: DATA  省略
2021-07-30T07:32:40.379Z INFO webdriver: RESULT  省略
2021-07-30T07:32:40.387Z INFO webdriver: COMMAND getElementText("0152bc52-dff5-4638-9979-e4689d0f71eb")
2021-07-30T07:32:40.387Z INFO webdriver: [GET] http://127.0.0.1:4723/wd/hub/session/27d30517-4d1e-4128-bb9e-733c5dd774d3/element/0152bc52-dff5-4638-9979-e4689d0f71eb/text
2021-07-30T07:32:40.413Z INFO webdriver: RESULT Happy Birthday, Sam!
    ✔ check message text (109ms)
2021-07-30T07:32:40.418Z INFO webdriver: COMMAND findElement("xpath", "//*[@resource-  省略
21-07-30T07:32:40.482Z INFO webdriver: RESULT  省略
2021-07-30T07:32:40.488Z INFO webdriver: COMMAND getElementText("17f89d91-f14e-47af-aebd-d365a9dfb67d")
2021-07-30T07:32:40.489Z INFO webdriver: [GET] http://127.0.0.1:4723/wd/hub/session/27d30517-4d1e-4128-bb9e-733c5dd774d3/element/17f89d91-f14e-47af-aebd-d365a9dfb67d/text
2021-07-30T07:32:40.511Z INFO webdriver: RESULT From Emma.
    ✔ check from text (94ms)
2021-07-30T07:32:40.512Z INFO webdriver: COMMAND deleteSession()
2021-07-30T07:32:40.513Z INFO webdriver: [DELETE] http://127.0.0.1:4723/wd/hub/session/27d30517-4d1e-4128-bb9e-733c5dd774d3

  3 passing (14s)

そのためwebdriverioのoptionにoutputDirキーを追加し、

outputDir: `${__dirname.replace(/__e2e__(\\|\/)wedio/, "")}__e2e__/`

のような記載してappium clientのlogは別の所に出力させるのがいいと思われる。

debbugerでのロケータ取得方法

e2eテストを実装していくにあたっては、要素を取得しそれに対して何らかのアクションを実行するがその要素を取得するためのロケータ情報はAppium Desktopで行う。
ロケータ情報を取得する時は、appium clientからAVD上にアプリを起動し、その状態で一時的にStopさせる必要があるのでnode のinspectオプション × debuggerを使う。

ソースコード上に以下のようにdebuggerを置き、node --inspect hoge.jsのようにテストを起動する。
(今回はmochaだが要領は同じで、debuggerを置き、mocha inspect --no-timeout1でテストを実行すればいい。)

describe('Create session and delete session', () => {
    let client;

    before(async () => {
        client = await wdio.remote(opts);
    });

    it('debugger', async () => {
        debugger;
    });

    after(async () => {
        await client.deleteSession();
    });

})

以下実際の流れ。

  1. appium serverとAVDを起動する
  2. テストを実行すると、Nodeのinspectの機能でコマンドラインで止まるのでそこで一旦放置
  3. Appium Desktopを起動する
  4. File > New Session Window... > Attach to Session...タブ > Attach to Sessionボタンで以下のような画面が表示されるのでここからロケータを取得するappium.png

※この時のポイントとしては、

  • newCommandTimeout: 300 など長い時間に設定する
    デフォルトでは 60(60 秒) になっているので、デバッグポイントで一時停止させた後、何も操作をしない(client から server にコマンドを送信しない)と 60 秒で session が切れてしまうため
    ※newCommandTimeoutはcapabilitiesのキーの1つ
    https://appium.io/docs/en/writing-running-appium/caps/
  • --no-timeout に設定する
    デフォルトでは 2s になっているので、mocha inspect --no-timeoutのように timeout しないようにしてからテスト実行しないと、appium desktop でロケータ情報などを取得している最中にデバッグが終了してしまう

のようにする事。

テスト対象アプリ

テスト対象アプリ① Happy Birthday Card App

アプリとしては特にユーザが操作する事は全くできないアプリで、画面にテキストが表示されているもの。
そのためテスト内容としてはアプリに表示されているテキストが期待通りか?をassertionするテストを作成。
ソースコードは以下。

CI

ローカル環境でE2Eテストが実行できているだけではあまり意味がないため、CI環境で実行できるようにCIのパイプラインを構築した。
せっかくなので、

  • CircleCi
  • GitHub Actions

の2つのCICDプラットフォームでE2Eテスト自動化のパイプラインを構築した。

CircleCi

CircleCiの場合は公式ページにある程度ヒントになるyamlがあるのでそれを参考にすればいい。
使うコンテナはUsing Android Images with the Machine Executorという便利なコンテナが標準で用意されているのでそれを使う。これを使うとAndroidアプリのbuildを行ったり、emulatorを作成・起動するのが簡単にできる。
(GitHub:https://github.com/CircleCI-Public/android-image-preview-docs

実際に作成したパイプラインのソースコードは以下。

GitHub Actions

GitHub Actionsの場合は、コンテナの選択を間違えると詰るので注意。大体CIを構築する時はubuntu-latestを使うと思うがこれだとAndroidのemulatorが起動できない。
そのためGitHub Actionsでは、macos-latestを使うようにする。
詳細はubuntu-latestでエラー『emulator: ERROR: x86 emulation currently requires hardware acceleration!』を使うを参照。

実際に作成したパイプラインのソースコードは以下。

※GitHub Actionsでのe2eテスト自動化は少し手間取った箇所があったのでそれを以下のトラブルシューティングに書き残しておく。

トラブルシューティング

sdkmanager, avdmanager コマンドの実行

sdkmanagerコマンドを実行しようと数するもエラー(GitHub Actions の JobURL はこれ

sdkmanager "system-images;android-28;default;x86"
avdmanager --verbose create avd -n test -k "system-images;android-28;default;x86"

/home/runner/work/_temp/af62e0b9-d807-4eb2-87a6-f1e203106b6c.sh: line 2: sdkmanager: command not found

単純にデフォルトでパスが通っていないようなので、どこにsdkmanagerがいるかを探す必要がある。
探すとsdkmanagerandroid_sdk/cmdline-tools/version/bin/にあるので、

$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager

のように実行するればよい。
参考:Android SDK コマンドライン ツール

ライセンスへの同意

sdkmanagerコマンドを実行すると Android SDK のライセンス同意が表示され、それに対し標準入力を行わないとエラーになる。
これは

echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "$SYSTEM_IMAGES"

のようにする事でコマンドからの質問に応答できる。

emulator コマンドの実行

emulatorandroid_sdk/emulator/にあるので

$ANDROID_HOME/emulator/emulator -avd test -verbose -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim

のように実行する。
参考:Android エミュレータ

※ちなみに、emulator 起動時に引数に色々オプションを付けられるがそれぞれ以下の通り。

コマンドライン オプション 説明
-avd avd name 起動する avd の名前を指定する
-verbose エミュレータの初期化メッセージをターミナル ウィンドウに出力させる
-no-window エミュレータのグラフィカル ウィンドウ表示を無効にする
-gpu mode グラフィック アクセラレーション タイプをmodeに指定する
-no-snapshot スナップショットの復元後すぐに AVD クロック時刻の修正を試みないようにする
-noaudio 仮想デバイスのオーディオ サポートを無効にする
-no-boot-anim 起動を高速化するため、エミュレータ起動中の起動アニメーションを無効にする

参考:コマンドラインからのエミュレータの起動
参考:コマンドラインからグラフィック アクセラレーションを設定する

emulatorがbootするまで待機する

emulator が起動するまでには少し時間がかかり、起動が完了してからテスト実行しないとテストが失敗する。
のでemulatorが起動完了する(boot completed)まで待つ必要がある。

emulator が起動完了すると以下のような log が出るが、

emulator: INFO: boot completed

これが出るまで待つには、

adb shell getprop init.svc.bootanim

というコマンドを利用すればいい。
実際の実装としては以下のようになる。

while [ "`adb shell getprop sys.boot_completed | tr -d '\r'`" != "1" ] ; do sleep 10 && echo "wait for boot..."; done

参考:Detect when Android emulator is fully booted
参考:【 tr 】コマンド――テキストファイルの文字を置換する/削除する

Background実行のjobのlogをArtifactに保存

CircleCi では background: true で backgroud 実行させてそのlogも見れるが、GitHub Actionsではbackgroundで実行させる事はできてもそのlogがGitHub Actionsのconsoleに出力されない。
ex) https://github.com/yuta-katayama-23/MobileAppE2ETest/runs/3191394079?check_suite_focus=true

そのためappium server、AVDのlogそれぞれをArtifactに保存するように実装した。

      - name: Start android emulator
        # https://developer.android.com/studio/run/emulator-commandline
        run: |
          mkdir ~/log
          $ANDROID_HOME/emulator/emulator -avd test -verbose -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim >> ~/log/emulator.log &

      # 省略

      - name: Start appium server
        run: |
          cd __e2e__
          npm run s:appium >> ~/log/appium.log &

      # 省略

      - name: Archive emulator and appium server logs
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: log
          path: ~/log

ubuntu-latest でエラー『emulator: ERROR: x86 emulation currently requires hardware acceleration!』

(前置き:ここにはubuntu-latestでCIを構築しようとして失敗した時の作業logも細かく書いてある。)

GitHub Actions も CircleCi と考え方は同じで、基本的にはローカルでエミュレータ起動・Appium Server の起動を行い、その後テスト実行を行う手順と同じ事をコマンドで行えば GitHub Actions 上でも E2Eテストが実行できる。

GitHub Actions の Runner ubuntu-latest元々インストール済みのものを見ると、Android のアプリ build・E2E テストに必要なものはほぼインストール済みで、インストールする必要があるのは appium だけ。
※ちなみに、GitHub Actions の Runner 一覧はここ

使うコンテナをubuntu-latestに決め、パイプライン実装して実行すると、以下のようなlogが出てエラーに(jobのURLはこれ。)
logの内容から単純にKVMが必要そうと分かり、logに出ているリンクにアクセス。

emulator: CPU Acceleration: DISABLED
emulator: CPU Acceleration status: KVM requires a CPU that supports vmx or svm
emulator: ERROR: x86 emulation currently requires hardware acceleration!
CPU acceleration status: KVM requires a CPU that supports vmx or svm
More info on configuring VM acceleration on Linux:
https://developer.android.com/studio/run/emulator-acceleration#vm-linux
General information on acceleration: https://developer.android.com/studio/run/emulator-acceleration.

Android 公式のドキュメントに沿って Ubuntu KVM のインストールを行っていく

Linux ベースのシステムは、KVM ソフトウェア パッケージを介して VM アクセラレーションをサポートしています。 Linux システムに KVM をインストールする手順を行い、KVM が有効になっていることを確認します。Ubuntu システムについては、Ubuntu KVM のインストールの説明をご覧ください。

Ubuntu KVM のインストールのサイトに従ってやっていく(GitHub ActionsのRunner上で)。

sudo apt-get install cpu-checker
sudo /usr/sbin/kvm-ok

でチェックすると、、、

INFO: Your CPU does not support KVM extensions
KVM acceleration can NOT be used

と言われ、使えないの・・・となるが、以下のような注意書きがあり、使える/使えないを示してるだけなので、install すれば OK そうと考える。

NOTE: You may see a message like "KVM acceleration can/can NOT be used". This is misleading and only means if KVM is currently available (i.e. "turned on"), not if it is supported.(「KVM アクセラレーションを使用できます/使用できません」のようなメッセージが表示される場合があります。 これは誤解を招く可能性があり、KVM が現在利用可能(つまり「オン」)である場合にのみ意味し、サポートされている場合はそうではありません。)

ubuntu-latest はCosmic (18.10) or later2なので、

sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils

を実行すればいい
その後、Add Users to Groupsを実行してvirsh list --allでインストールが成功したか?を確認するもエラーが変わらず・・・

GitHub Actions の Marketplace にあるGitHub Action - Android Emulator Runnerを見ると、

The old ARM-based emulators were slow and are no longer supported by Google. The modern Intel Atom (x86 and x86_64) emulators require hardware acceleration (HAXM on Mac & Windows, QEMU on Linux) from the host to run fast. This presents a challenge on CI as to be able to run hardware accelerated emulators within a docker container, KVM must be supported by the host VM which isn't the case for cloud-based CI providers due to infrastructural limits.(古い ARM ベースのエミュレーターは低速であり、Google ではサポートされなくなりました。 最新の IntelAtom(x86 および x86_64)エミュレーターは、高速で実行するために、ホストからのハードウェアアクセラレーション(Mac および Windows では HAXM、Linux では QEMU)を必要とします。これは、Docker コンテナー内でハードウェアアクセラレーションエミュレーターを実行できるようにするという CI の課題を提示します。)
The macOS VM provided by GitHub Actions has HAXM installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android tests directly on the VM. You can also achieve this on a self-hosted Linux runner, but it will need to be on a compatible instance that allows you to enable KVM - for example AWS EC2 Bare Metal instances.(GitHubActions が提供する macOSVM には HAXM がインストールされているため、新しい AVD インスタンスを作成し、ハードウェアアクセラレーションを使用してエミュレーターを起動し、VM で Android テストを直接実行できます。 セルフホストの Linux ランナーでもこれを実現できますが、KVM を有効にできる互換性のあるインスタンス(AWS EC2 ベアメタルインスタンスなど)で実行する必要があります。)
It is recommended to run this action on a macOS VM, e.g. macos-latest or macos-10.15 to take advantage of hardware accleration support provided by HAXM.(このアクションは、macOSVM で実行することをお勧めします。 macos-latest または macos-10.15 は、HAXM が提供するハードウェアアクセラレーションサポートを利用します。)

とか書いてあり、GitHub Actions 標準のubuntu-latestでやるのは不可能である事が分かり方針転換でmacos-latestに変更した。

macOS で VM アクセラレーションを設定するに従って

kextstat | grep intel

で Intel HAXM カーネル拡張機能が使えるか?を確認すると、

121 0 0xffffff7f82c2f000 0x28000 0x28000 com.intel.kext.intelhaxm (7.6.5) 9C4BE7CB-745A-326F-81FF-6D7D61B68F87 <8 6 5 3 1>

と表示されたので OKそう。
その後emulator起動をしてみるとうまくいった。

参考文献

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4