Android
Xamarin
FirebaseTestLab

Xamarinで作ったAndroidアプリをFirebase Test Labでテストする

概要

Xamarin でビルドした Android アプリでも、自動UIテストを Firebase Test Lab でやりたい。
その方法を調査した。

開発環境

テストコード作成は Android Studio 3.1.3 を使用。
Xamarin でビルドする側はバージョン関係なく大丈夫と思う。

方法

まず、テストコードを書くためのプロジェクトを Android Studio で作る。
Build.gradle (app配下の方)に、以下を追記。

// apkをXamarinでビルドしたものに差し替える
import java.nio.file.Files
import java.nio.file.StandardCopyOption

gradle.projectsEvaluated {
    // build variantが確定してから処理するため、projectEvaluatedのタイミングで処理する。

    project.tasks.all { task ->
        // assembleXXXX タスクの doLast に処理を埋め込む

        if (task.getName().startsWith("assemble")) {
            // assemble以降がvariant名と一致するタスクが実際にビルドするタスク

            def variantName = task.getName().replace("assemble", "")
            def variant = android.applicationVariants.matching { variant -> (variant.getName().equalsIgnoreCase(variantName)) }[0]
            if (variant != null) {
                // build variantが存在する場合、出力先のパスを取得する
                def inputPath = project.projectDir.toPath().resolve("replace").resolve(variant.getName()).resolve("replace.apk")
                def outputFile = variant.outputs[0]?.outputFile
                if (outputFile != null) {
                    task.doLast {
                        if (Files.exists(inputPath)) {
                            logger.lifecycle("Replace apk from \"" + inputPath + "\", to \"" + outputFile + "\"")
                            Files.copy(inputPath, outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
                        } else {
                            throw new GradleException("Tried replacing apk, but did not exist apk at \"" + inputPath + "\"")
                        }
                    }
                }
            }
        }
    }
}

gradle.projectsEvaluatedを使い、doLastでテストコードのビルド後にapkを置き換える。

あとは、Xamarin でビルドしたreplace.apkを、以下に配置しておけばよい。
/app/replace/≪Build Variant≫
例えば通常デバッグビルドの場合は、/app/replace/debug に置く。
ss.png
これでテストコードを実行すると、Xamarin 製Androidアプリを Firebase Test Lab の Instrumentation Test などでテストできる。

課題

テストコード側で R.java に R.id.xxxxx とかでアクセスできない問題が発生する。
以下のようにして対応する。

@IdRes
public static int getViewId(@NonNull String stringId) {
    Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
    String packageName = context.getPackageName();
    Resources resources = context.getResources();

    return resources.getIdentifier(stringId, "id", packageName);
}

補足

React Native や Flutter など他のクロスプラットフォーム手法で作成した apk もOKかどうかは試していませんが、ネイティブと同じUIレンダリングなら可能だろうと思います。
ただし、Xamarin と異なる課題が生じる可能性も高いです。