Open30

Roborazziを試してみる

stoostoo

なるほど?Showkaseというのを使うとPreview関数のComposableをスクリーンショットテストに使えるのか

Showkase を採用した理由としては、Preview 関数で生成した Composable をスクリーンショットテストにそのまま使用でき、コード削減できると考えたためです。

stoostoo

UIをレンダリングしたりそれを見つけられるようにしてくれる感じかな

Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements.

stoostoo

ほえー、デザインシステムのドキュメント化みたいなのもできるのか。どうやるのかわからないけど気になる

stoostoo

とりあえず記事を参考に手元のサンプルコードに導入してみる

stoostoo

KSPのバージョンがAGPとあってなかったので更新

stoostoo

UnitTestってどこに追加するんだっけ...(毎回忘れる)

stoostoo

任意のモジュール > src > new > Directoryでtest/java(kotlin)にすればいいのか

stoostoo

Showkase.getMetadataが参照エラーになる、と思ったらビルドしたら解決された。autogeneratedされるやつっぽい

stoostoo

なるほど、モジュールの定義はこのために必要なのか

This is relevant because we generate the Showkase related classes in the package of the root module and we need to be able to access the UI elements across all the sub modules. This is only possible from the root module as it typically has a dependency on all the sub-modules.

https://github.com/airbnb/Showkase?tab=readme-ov-file#if-you-want-showkase-to-be-available-only-in-debug-builds-recommended-and-practical-for-most-use-cases

stoostoo

デフォだと以下の場所に生成してくれるっぽい?

const val DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH: String = "build/outputs/roborazzi"

stoostoo

なんかテストが失敗する...

java.lang.NoSuchMethodError: 'float android.text.TextPaint.getLetterSpacing()'

stoostoo

これを追加し忘れてたっぽい

    testOptions {
        unitTests {
            this.isIncludeAndroidResources = true
        }
    }
stoostoo

いけたっぽい。
追加したUnitTestを実行したらちゃんと所定の位置にPreview 関数分のpngファイルが作成されてる

stoostoo

普通のPreview関数って多分Screen単位とかでしか作らないと思うので、AppBarとかも含めたスクショが撮りたかったらまた別の方法を使うって感じになるのかな?
明日他のサンプルも見てみよう。DroidKaigiのConferenceAppとNiaも使ってた気がする

stoostoo

今日は参考文献も見てみる

stoostoo

https://zenn.dev/aldagram_tech/articles/d6d7e272628b9b

なるほどーーーー、差分を比較するとかはRoborazzi側のgradle taskとして用意してくれてるのか

./gradlew recordRoborazziDebug
スクリーンショットを保存して、 build/outputs/roborazzi 以下のディレクトリに保存
./gradlew compareRoborazziDebug
build/outputs/roborazzi 以下のディレクトリに保存されているスクリーンショットをと、テストから生成されるスクリーンショットを比較する。差分があった場合は ***_compare.png の名前で差分の画像を生成
./gradlew verifyRoborazziDebug
保存されているスクリーンショットと、テストから生成されるスクリーンショットを比較して、差分があった場合はテスト失敗とする
./gradlew verifyAndRecordRoborazziDebug
保存されているスクリーンショットをと、テストから生成されるスクリーンショットを比較する。差分があった場合は生成されたスクリーンショットを新しい比較元のスクリーンショットとする

stoostoo

見方があんまよくわからんけど何かしらDiffっぽいものは生成された

stoostoo

なるほど?AppBarがScreenの外にある場合の全体画面のスクショとかもこの感じでやればいいのかな

スクリーンショットの保存は captureRoboImage() を呼び出すことで行えます。Theme の適用などの共通の処理を checkComposeScreen という名前で切り出し、Composeの画面毎に checkSettingsScreen などのテストから呼び出すようにしています。

stoostoo

NiaのNiaAppScreenSizesScreenshotTestsらへんを参考にすると良さそう?

stoostoo

似た書き方しているな。明日これ試してみよう。

    private fun testNiaAppScreenshotWithSize(width: Dp, height: Dp, screenshotName: String) {
        composeTestRule.setContent {
            CompositionLocalProvider(
                LocalInspectionMode provides true,
            ) {
                TestHarness(size = DpSize(width, height)) {
                    BoxWithConstraints {
                        NiaTheme {
                            val fakeAppState = rememberNiaAppState(
                                windowSizeClass = WindowSizeClass.calculateFromSize(
                                    DpSize(maxWidth, maxHeight),
                                ),
                                networkMonitor = networkMonitor,
                                userNewsResourceRepository = userNewsResourceRepository,
                                timeZoneMonitor = timeZoneMonitor,
                            )
                            NiaApp(fakeAppState)
                        }
                    }
                }
            }
        }

        composeTestRule.onRoot()
            .captureRoboImage(
                "src/testDemo/screenshots/$screenshotName.png",
                roborazziOptions = DefaultRoborazziOptions,
            )
    }

stoostoo

あんまよくわかってないのが、ComposeがViewModelとかに依存してる状態でPreviewしようとするとエラーになるだった気がするんだけど、このcaptureRoboImageだとエラーにならない?composeTestRuleでActivityをセットしているとViewModelなどに依存していても問題なくキャプチャできる???

stoostoo

最後にCI/CDに組み込むステップを試し中 GithubActionsなんもわからん

stoostoo

↑で紹介されてる以下を手元のymlに組み込んで試し中

on:
  push:

jobs:
  unit_test_android:
    steps:

...
    - uses: actions/upload-artifact@v3
      if: ${{ always() }}
      with:
        name: golden-screenshot
        path: |
          **/build/outputs/roborazzi
        retention-days: 14
...```
stoostoo

AGPに対してJDKが古いと言われた。しばらくメンテしてなかった自分が悪い

Failed to apply plugin 'com.android.internal.application'.
Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
Your current JDK is located in /opt/hostedtoolcache/Java_Adopt_jdk/11.0.22-7/x64

stoostoo

記述を修正してリトライ

      - name: Set up JDK
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '11'