Galapagos Tech Blog

株式会社ガラパゴスのメンバーによる技術ブログです。

Roborazziでスクリーンショットテストを導入した話

サービス開発パートナー事業部 Androidエンジニアの務台です。 最近はモンハンNowのために散歩する日々が続いています。本編では双剣使いだったので早く追加してほしいなぁと思う今日この頃です。


皆さんのプロジェクトではスクリーンショットテストは使っていますか?

私が担当しているプロジェクトでは今までやってこなかったのですが、満を持して導入することができましたので、 その中での躓きを共有できればと思います。

結論

  • Compose Material3のバージョンが1.0.1未満の場合はgradleへ以下の記述が必要になる
configurations.all {
    resolutionStrategy.force libs.compose.material3
}

UIテスト導入の背景

Compose Material3のバージョンを1.0.0-alpha13から1.0.2に上げたところ…

1.0.0-alpha13 => 1.0.2

はい。HorizontalPagerのインジケータの位置がずれてしまいました。

この変化はたまたま見つけることができましたが、全コンポーネントを目視で確認するのはとても大変ですし、確認漏れが発生する恐れがあります。

このような予期しない変更を検知するため、Material3のバージョンアップ前にUIテストを導入し、バージョンアップによる表示崩れが起きないようにしました。

Roborazziの導入

現在だとUIテストを行うときに使用するライブラリとしては

のいずれかが選択肢として挙がるかと思いますが、今回は以下の理由によりRoborazziを使用することにしました。

  • Paparazziより後発であり、Paparazziで使用できる機能を保有していること
  • Hiltを使用したDIが使用可能であること
  • DroidKaigi 2023アプリに使用されており、この実装を参考にできること

導入手順

導入手順としてはGitHubに書いてあるとおり簡単で、

Robolectricを追加して

testImplementation "org.robolectric:robolectric:4.10.3"

プロジェクトのgradleにプラグインを追加して

plugins {
   id "io.github.takahirom.roborazzi" version "1.6.0" apply false
}

モジュールのgradleにもプラグインを追加する。

plugins {
    id 'io.github.takahirom.roborazzi'
}

./gradlew test実行時にスクリーンショットテストも行うために gradle.propertiesに以下を追記する。

roborazzi.test.verify=true

ここまででライブラリの導入は完了です。

あとはキャプチャの保存先や判定の閾値等を管理するクラスを作成して

class LaunchScreenShotRobot(
    private val composeTestRule: ComposeContentTestRule,
) {
    private val roborazziOptions = RoborazziOptions(
        compareOptions = RoborazziOptions.CompareOptions(
            changeThreshold = 0.001f,
        ),
    )

    fun setupContent(
        content: @Composable () -> Unit,
    ) {
        composeTestRule.setContent {
            MaterialTheme {
                Surface {
                    content()
                }
            }
        }
    }

    fun captureLaunchScreen(
        fileName: String,
    ) {
        composeTestRule.onRoot().captureRoboImage(
            filePath = "screenshots/$fileName.png",
            roborazziOptions = roborazziOptions,
        )
    }
}

スクリーンショットテストを行うためのユニットテストを書く。

@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(
    qualifiers = RobolectricDeviceQualifiers.Pixel7Pro,
    sdk = [33],
)
class SampleScreenShotTest {
    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun checkSampleScreen() {
        with(LaunchScreenShotRobot(composeTestRule)) {
            setupIntegrationContent {
                SampleCompose(modifier = Modifier.fillMaxSize())
            }
            captureLaunchScreen("SampleScreen")
        }
    }
}

これにより./gradlew recordRoborazziDebugスクリーンショットを/screenshots/配下に保管し、 ./gradlew test実行でUIの変更も検知することができるようになります。

…通常であれば、、、

沼った

Roborazziを依存関係へ追加後ビルドすると、プロジェクトで使用しているMaterial3のバージョンが1.0.0-alpha13なのにも関わらず、 上記と同様の位置ズレ問題が発生するようになってしまいました。 これはRoborazziがMaterial3 1.0.1に依存していることが原因でした。(原因調査に1日以上かかってしまった…)

Roborazziでも1.0.0-alpha13を使用すればOKなので、以下の記述をgradleへ追加します。

configurations.all {
    resolutionStrategy.force libs.compose.material3
}

これによってRoborazziでも使用するMaterial3のバージョンが1.0.0-alpha13に強制され、UIが崩れる前のHorizontalPagerの画像を取得することができました。

試してみる

これでMaterial3のバージョンアップ前、バージョンアップ後でスクリーンショットテストによるUI崩れの検知ができるようになったはずなので試してみます。

バージョンアップ前に./gradlew recordRoborazziDebugを実行し、 バージョンアップ後に./gradlew testを実行してみると…

Roborazzi: /screenshots/Sample.png is changed.

無事、テストに失敗しました。 build/outputs/Roborazzi/Sample_compare.pngを見てみると

と、しっかり差分が検知されていることが確認できます。

まとめ

躓きはあったものの、無事Roborazziによるスクリーンショットテストを導入し、Material3のバージョンアップによるUI崩れを検知することができるようになりました。 一度入れてしまえばあとは新画面追加の度にスクリーンショットテストの実装も追加していくだけなので簡単にUI崩れを防ぐことができそうです。

まだまだ使用していないRoborazziの機能があるので、今後は

  • Hiltを利用したRobotのDI
  • ライトモード、ダークモードでのスクリーンショットテスト
  • RoborazziRuleを用いたgifでの動作中画像の比較

あたりを追加できたらいいなと思っています。


ガラパゴスでは、一緒に働く仲間を募集しています!

hrmos.co