Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Roborazziを最大限に活用する(導入編)
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Swimmy
December 01, 2023
370
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Roborazziを最大限に活用する(導入編)
12/1のDroidKaigi.collect { #7@TOkyo } で登壇した資料です
Swimmy
December 01, 2023
More Decks by Swimmy
See All by Swimmy
Android16 Migration Stories ~Building a Pattern for Android OS upgrades~
reoandroider
0
270
違いがワカルKotlinプログラマーへの道
reoandroider
0
170
違いのワカル Kotlinプログラマーへの道
reoandroider
0
280
[Jetpack Compose] 原理から見るSkippable対応
reoandroider
1
380
Androdアプリ開発におけるネストスクロール問題と向き合うの説明用途
reoandroider
0
3.5k
Androdアプリ開発におけるネストスクロール問題と向き合うの説明用途
reoandroider
0
3.3k
Androdアプリ開発におけるネストスクロール問題と向き合うの説明用途~NestedScrollInteropConnectionは何してるの?編~
reoandroider
0
3k
Master of NestedScroll
reoandroider
0
16k
Jetpack_Composeで_半円のプログレスバーを作った話
reoandroider
0
480
Featured
See All Featured
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
250
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
1
540
Typedesign – Prime Four
hannesfritz
42
3.2k
WCS-LA-2024
lcolladotor
0
840
Odyssey Design
rkendrick25
PRO
2
850
Git: the NoSQL Database
bkeepers
PRO
432
67k
Statistics for Hackers
jakevdp
799
230k
Music & Morning Musume
bryan
48
7.4k
sira's awesome portfolio website redesign presentation
elsirapls
0
430
Why Our Code Smells
bkeepers
PRO
340
58k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
560
Faster Mobile Websites
deanohume
310
32k
Transcript
Harada Reo @ すいみー DroidKaigi @Tokyo .collect {} Roborazziを最大限に活用する (導入編)
すいみー CyberAgent Inc. Ameba どすこい塾 DroidKaigi Staff RunningReo(X) 自己紹介 /
Introduction
このLTのゴール 聞いてくれた人が Roborazziを スムーズに導入できる状態
Roborazzi Roborazziとは? takahiromさんによる 静的なスクリーンショットテストライブラリ https://github.com/takahirom/roborazzi
目次 / Index プロダクトが抱えている課題 2 3 今後の展望 1 Roborazzi導入手順
プロダクトが抱えている課題 1
紹介 Ameba ブログや話題の芸能ニュースを毎日お届け! https://play.google.com/store/apps/details?id=jp.ameba&hl=ja&gl=US
紹介 リリースから10年以上運用している
紹介 Amebaでは Roborazziを 導入中・・・
課題点 アプリの画面数が多く QCさんによる デグレ確認のテスト項目が 膨大な量になっている
解決アプローチ Roborazziを用いることで 目視確認を自動化して 品質担保を目指す
Roborazzi導入手順 2
前提の話 ・Hilt対応していると楽 ・Jetpack ComposeもAndroidViewも 相互運用も対応しているよ
前提の話 ・Hilt対応していると楽 ・Jetpack ComposeもAndroidViewも 相互運用も対応しているよ
前提の話 ・Hilt対応していると楽 ・Jetpack ComposeもAndroidViewも 相互運用も対応しているよ Activityをベースにテストする時にテスト用のApplicationクラスを Robolectricに登録するなど冗長的な作業が発生する
前提の話 ・Hilt対応していると楽 ・Jetpack ComposeもAndroidViewも 相互運用も対応しているよ
前提の話 ・Hilt対応していると楽 ・Jetpack ComposeもAndroidViewも 相互運用も対応しているよ ComposeRuleが内部でActivityScenarioを用いているため // 内部実装 inline fun
<reified A : ComponentActivity> createAndroidComposeRule(): AndroidComposeTestRule<ActivityScenarioRule<A>, A> { return createAndroidComposeRule(A::class.java) }
依存関係の追加 // root build.gradle buildScript { dependencies { classPath =
"io.github.takahirom.roborazzi:roborazzi-gradle-plugin: [version]" } }
依存関係の追加 // 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]" }
依存関係の追加 // 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]" }
依存関係の追加 // 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]" }
依存関係の追加 // 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]" }
依存関係の追加 // 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]" }
依存関係の追加 // 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]" }
依存関係の追加 // 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]" }
テストクラス作成 @RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class BookmarkScreenShotTest { ...
テストクラス作成 @RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class BookmarkScreenShotTest { ...Robolectric4.10-alphaから使える実装 ネイティブのグラフィックをサポート
テストクラス作成 ... 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) } }
テストクラス作成 ... 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の登録を行う
テストクラス作成 ... 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...
テストクラス作成 ... 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() }
テストクラス作成 ... 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を呼び出す
テストクラス作成 ... 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と機種を指定
テストクラス作成 ... 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を起動させる
テストクラス作成 ... 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でスクショを撮る
テスト実行 ./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト
./gradlew verifyAndRecordRoborazziDebug 差分があるかどうかとスクショを撮る
テスト実行 ./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト
./gradlew verifyAndRecordRoborazziDebug 差分があるかどうかとスクショを撮る
./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト ./gradlew
verifyAndRecordRoborazziDebug 差分があるかどうかとスクショを撮る テスト実行
./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト ./gradlew
verifyAndRecordRoborazziDebug 差分があるかどうかとスクショを撮る テスト実行
./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト ./gradlew
verifyAndRecordRoborazziDebug 差分があるかどうかとスクショを撮る テスト実行
./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト ./gradlew
verifyAndRecordRoborazziDebug 差分があるかどうかとスクショを撮る テスト実行
./gradlew recordRoborazziDebug 初期状態のスクショを撮る・比較におけるbefore ./gradlew compareRoborazziDebug 差分のスクショを撮る ./gradlew verifyRoborazziDebug 差分があるかどうかのテスト ./gradlew
verifyAndRecordRoborazziDebug 差分があるかどうかとある場合にスクショを撮る テスト実行
今後の展望 3
このLTのゴール 聞いてくれた人が Roborazziを スムーズに導入できる状態
まとめ 躓いて何日かハマったこともあったが 一度スクショ撮れるとあとは楽
まとめ 導入始めたばかりなので まだ検証したいことはある ・ベンチマークの設定 ・CIの時間への影響 ・UIテストとの兼ね合い
まとめ 品質担保において活用できるが エンジニアだけが使う ツールで消費されるのは もったいない気がする
まとめ デザイナーとのコミュニケーションを 円滑にする手段として活用したい (カタログやデザインシステムの準拠など)
まとめ 現状スクショのdiff詳細は見れないので OSSやEspressoのdump出力を活用して 最大限活用する方法を見つけていきたい
Harada Reo @ すいみー Roborazziを最大限に活かす (運用編)
to be continued