Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Roborazziを最大限に活用する(導入編)

Swimmy
December 01, 2023
69

 Roborazziを最大限に活用する(導入編)

12/1のDroidKaigi.collect { #7@TOkyo } で登壇した資料です

Swimmy

December 01, 2023
Tweet

More Decks by Swimmy

Transcript

  1. Harada Reo @ すいみー
    DroidKaigi @Tokyo
    .collect {}
    Roborazziを最大限に活用する
    (導入編)

    View full-size slide

  2. すいみー
    CyberAgent Inc.
    Ameba
    どすこい塾
    DroidKaigi Staff
    RunningReo(X)
    自己紹介 / Introduction

    View full-size slide

  3. このLTのゴール
    聞いてくれた人が
    Roborazziを
    スムーズに導入できる状態

    View full-size slide

  4. Roborazzi
    Roborazziとは?
    takahiromさんによる
    静的なスクリーンショットテストライブラリ
    https://github.com/takahirom/roborazzi

    View full-size slide

  5. 目次 / Index
    プロダクトが抱えている課題

    3 今後の展望

    Roborazzi導入手順

    View full-size slide

  6. プロダクトが抱えている課題
    1

    View full-size slide

  7. 紹介
    Ameba
    ブログや話題の芸能ニュースを毎日お届け!
    https://play.google.com/store/apps/details?id=jp.ameba&hl=ja&gl=US

    View full-size slide

  8. 紹介
    リリースから10年以上運用している

    View full-size slide

  9. 紹介
    Amebaでは
    Roborazziを
    導入中・・・

    View full-size slide

  10. 課題点
    アプリの画面数が多く
    QCさんによる
    デグレ確認のテスト項目が
    膨大な量になっている

    View full-size slide

  11. 解決アプローチ
    Roborazziを用いることで
    目視確認を自動化して
    品質担保を目指す

    View full-size slide

  12. Roborazzi導入手順
    2

    View full-size slide

  13. 前提の話
    ・Hilt対応していると楽
    ・Jetpack ComposeもAndroidViewも
     相互運用も対応しているよ

    View full-size slide

  14. 前提の話
    ・Hilt対応していると楽
    ・Jetpack ComposeもAndroidViewも
     相互運用も対応しているよ

    View full-size slide

  15. 前提の話
    ・Hilt対応していると楽
    ・Jetpack ComposeもAndroidViewも
     相互運用も対応しているよ
    Activityをベースにテストする時にテスト用のApplicationクラスを
    Robolectricに登録するなど冗長的な作業が発生する

    View full-size slide

  16. 前提の話
    ・Hilt対応していると楽
    ・Jetpack ComposeもAndroidViewも
     相互運用も対応しているよ

    View full-size slide

  17. 前提の話
    ・Hilt対応していると楽
    ・Jetpack ComposeもAndroidViewも
     相互運用も対応しているよ
    ComposeRuleが内部でActivityScenarioを用いているため
    // 内部実装
    inline fun
    createAndroidComposeRule():
    AndroidComposeTestRule, A> {
    return createAndroidComposeRule(A::class.java)
    }

    View full-size slide

  18. 依存関係の追加
    // root build.gradle
    buildScript {
    dependencies {
    classPath = "io.github.takahirom.roborazzi:roborazzi-gradle-plugin: [version]"
    }
    }

    View full-size slide

  19. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  20. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  21. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  22. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  23. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  24. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  25. 依存関係の追加
    // module build.gradle
    apply plugin: "io.github.takahirom.roborazzi"
    android {
    testOptions { unitTests { includeAndroidResources = true } }
    }
    dependencies {
    testImplementation "androidx.test.ext:junit-ktx:[version]"
    testImplementation "org.robolectric:robolectric:[4.10-alpha以上]"
    testImplementation "io.github.takahirom.roborazzi:roborazzi:[version]"
    testImplementation "androidx.compose.ui:ui-test-junit4:[version]"
    }

    View full-size slide

  26. テストクラス作成
    @RunWith(AndroidJUnit4::class)
    @GraphicsMode(GraphicsMode.Mode.NATIVE)
    class BookmarkScreenShotTest {
    ...

    View full-size slide

  27. テストクラス作成
    @RunWith(AndroidJUnit4::class)
    @GraphicsMode(GraphicsMode.Mode.NATIVE)
    class BookmarkScreenShotTest {
    ...Robolectric4.10-alphaから使える実装
    ネイティブのグラフィックをサポート

    View full-size slide

  28. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    @get:Rule(order = 1)
    val addActivityToRobolectricRule = object : TestWatcher() {
    override fun starting(description: Description?) {
    super.starting(description)
    val appContext: Application = ApplicationProvider.getApplicationContext()
    val activityInfo = ActivityInfo().apply {
    name = ComponentActivity::class.java.name
    packageName = appContext.packageName
    }
    shadowOf(appContext.packageManager).addOrUpdateActivity(activityInfo)
    }
    }

    View full-size slide

  29. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    @get:Rule(order = 1)
    val addActivityToRobolectricRule = object : TestWatcher() {
    override fun starting(description: Description?) {
    super.starting(description)
    val appContext: Application = ApplicationProvider.getApplicationContext()
    val activityInfo = ActivityInfo().apply {
    name = ComponentActivity::class.java.name
    packageName = appContext.packageName
    }
    shadowOf(appContext.packageManager).addOrUpdateActivity(activityInfo)
    }
    }
    RobolectricへのActivityの登録を行う

    View full-size slide

  30. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    @get:Rule(order = 1)
    val addActivityToRobolectricRule = object : TestWatcher() {
    override fun starting(description: Description?) {
    super.starting(description)
    val appContext: Application = ApplicationProvider.getApplicationContext()
    val activityInfo = ActivityInfo().apply {
    name = ComponentActivity::class.java.name
    packageName = appContext.packageName
    }
    shadowOf(appContext.packageManager).addOrUpdateActivity(activityInfo)
    }
    }
    登録できてないとエラーが発生する
    java.lang.RuntimeException: Unable to resolve activity for Intent...

    View full-size slide

  31. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    ...
    @get:Rule(order = 2)
    val composeRule = createComposeRule()
    @Test
    @Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel7)
    fun captureSampleScreen() {
    composeRule.setContent { SampleTheme { SampleScreen(...) } }
    composeRule.onNode(isRoot()).captureRoboImage()
    }

    View full-size slide

  32. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    ...
    @get:Rule(order = 2)
    val composeRule = createComposeRule()
    @Test
    @Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel7)
    fun captureSampleScreen() {
    composeRule.setContent { SampleTheme { SampleScreen(...) } }
    composeRule.onNode(isRoot()).captureRoboImage()
    }
    createComopseRuleを呼び出す

    View full-size slide

  33. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    ...
    @get:Rule(order = 2)
    val composeRule = createComposeRule()
    @Test
    @Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel7)
    fun captureSampleScreen() {
    composeRule.setContent { SampleTheme { SampleScreen(...) } }
    composeRule.onNode(isRoot()).captureRoboImage()
    }
    スクショする端末のSDKと機種を指定

    View full-size slide

  34. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    ...
    @get:Rule(order = 2)
    val composeRule = createComposeRule()
    @Test
    @Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel7)
    fun captureSampleScreen() {
    composeRule.setContent { SampleTheme { SampleScreen(...) } }
    composeRule.onNode(isRoot()).captureRoboImage()
    }
    Composeを起動させる

    View full-size slide

  35. テストクラス作成
    ...
    class BookmarkScreenShotTest {
    ...
    @get:Rule(order = 2)
    val composeRule = createComposeRule()
    @Test
    @Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel7)
    fun captureSampleScreen() {
    composeRule.setContent { SampleTheme { SampleScreen(...) } }
    composeRule.onNode(isRoot()).captureRoboImage()
    }
    captureRoboImageでスクショを撮る

    View full-size slide

  36. テスト実行
    ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとスクショを撮る

    View full-size slide

  37. テスト実行
    ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとスクショを撮る

    View full-size slide

  38. ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとスクショを撮る
    テスト実行

    View full-size slide

  39. ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとスクショを撮る
    テスト実行

    View full-size slide

  40. ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとスクショを撮る
    テスト実行

    View full-size slide

  41. ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとスクショを撮る
    テスト実行

    View full-size slide

  42. ./gradlew recordRoborazziDebug
    初期状態のスクショを撮る・比較におけるbefore
    ./gradlew compareRoborazziDebug
    差分のスクショを撮る
    ./gradlew verifyRoborazziDebug
    差分があるかどうかのテスト
    ./gradlew verifyAndRecordRoborazziDebug
    差分があるかどうかとある場合にスクショを撮る
    テスト実行

    View full-size slide

  43. 今後の展望
    3

    View full-size slide

  44. このLTのゴール
    聞いてくれた人が
    Roborazziを
    スムーズに導入できる状態

    View full-size slide

  45. まとめ
    躓いて何日かハマったこともあったが
    一度スクショ撮れるとあとは楽

    View full-size slide

  46. まとめ
    導入始めたばかりなので
    まだ検証したいことはある
    ・ベンチマークの設定
    ・CIの時間への影響
    ・UIテストとの兼ね合い

    View full-size slide

  47. まとめ
    品質担保において活用できるが
    エンジニアだけが使う
    ツールで消費されるのは
    もったいない気がする

    View full-size slide

  48. まとめ
    デザイナーとのコミュニケーションを
    円滑にする手段として活用したい
    (カタログやデザインシステムの準拠など)

    View full-size slide

  49. まとめ
    現状スクショのdiff詳細は見れないので
    OSSやEspressoのdump出力を活用して
    最大限活用する方法を見つけていきたい

    View full-size slide

  50. Harada Reo @ すいみー
    Roborazziを最大限に活かす
    (運用編)

    View full-size slide

  51. to be continued

    View full-size slide