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
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.4k
Androdアプリ開発におけるネストスクロール問題と向き合うの説明用途
reoandroider
0
3.3k
Androdアプリ開発におけるネストスクロール問題と向き合うの説明用途~NestedScrollInteropConnectionは何してるの?編~
reoandroider
0
3k
Master of NestedScroll
reoandroider
0
16k
Jetpack_Composeで_半円のプログレスバーを作った話
reoandroider
0
470
Featured
See All Featured
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Paper Plane
katiecoart
PRO
2
53k
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
510
Building the Perfect Custom Keyboard
takai
2
870
Optimising Largest Contentful Paint
csswizardry
37
3.9k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
250
Automating Front-end Workflow
addyosmani
1369
210k
Side Projects
sachag
455
43k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
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