Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
VRTをプロダクトに導入するまでのお話
Search
gotlin
May 09, 2024
370
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
VRTをプロダクトに導入するまでのお話
gotlin
May 09, 2024
More Decks by gotlin
See All by gotlin
KotlinConf2025現地参加 と Compose HotReload導入記
goutarouh
0
42
Applicationクラスのライフサイクルに気をつけよう
goutarouh
0
85
Conference-app-2024の良さげな実装を勝手にいくつか紹介する
goutarouh
0
88
Drag & Drop in LazyColumn
goutarouh
0
960
Featured
See All Featured
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
220
First, design no harm
axbom
PRO
2
1.2k
Scaling GitHub
holman
464
140k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
280
Claude Code のすすめ
schroneko
67
230k
Making Projects Easy
brettharned
120
6.7k
Ruling the World: When Life Gets Gamed
codingconduct
0
270
Designing Experiences People Love
moore
143
24k
Designing for Timeless Needs
cassininazir
1
270
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
550
A designer walks into a library…
pauljervisheath
211
24k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
600
Transcript
@gotlinan 2024/05/07 (⽕) DroidKaigi.collect { #8@Tokyo } VRTをプロダクトに 導⼊するまでのお話
所属: KINTOテクノロジーズ株式会社(通称KTC) モバイル開発グループ 開発: my route X(twitter): @gotlinan 趣味: バイク
my routeのVRT導⼊までの歴史 2024/4末~ 2023/11 2024/1 2023/12 2024/2 チーム内で VRT導⼊しちゃう〜? の声が出始める
Roborazziを 個⼈プロジェクト で導⼊ my routeでバグ発⽣ VRTがあれば 防げたかも。。。 my routeに 試験的に導⼊ とあるモジュールで実験 本格的実装 & 運⽤開始
Roborazzi導⼊に⾃信満々 2024/4末~ 2023/11 2024/1 2023/12 2024/2 チーム内で VRT導⼊しちゃう〜? の声が出始める Roborazziを
個⼈プロジェクト に導⼊ my routeでバグ発⽣ VRTがあれば 防げたかも。。。 my routeに 試験的に導⼊ とあるモジュールで実験 本格的実装 & 運⽤開始
Compose で動作確認 @RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class TestClass { @get:Rule val composeTestRule
= createComposeRule() @Test fun captureTestScreen() { composeTestRule.setContent { TestScreen(state = TEST_DATA) } composeTestRule.onRoot().captureRoboImage() } }
my routeの現在の構成 Activity Fragment A Fragment B xml (DataBinding) Compose
Compose xml (DataBinding)
Fragment × DataBinding × Hilt でVRTを⾏う必要がある
Fragment × DataBinding × Hilt で動作確認 @RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class TestClass
{ @Test fun captureTestFragment() { launchFragmentInContainer<TestFragment>() onView(withId(R.id.testFragment)).captureRoboImage() } }
Fragment × DataBinding × Hilt で動作確認 // https://issuetracker.google.com/issues/126775542 kaptTest("androidx.databinding:databinding-compiler:$version")
Fragment × DataBinding × Hilt で動作確認
Fragment × DataBinding × Hilt で動作確認 @AndroidEntryPoint class HiltTestActivity :
AppCompatActivity() https://github.com/android/architecture-samples/blob/dev- hilt/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/HiltExt.kt
Fragment × DataBinding × Hilt で動作確認 inline fun <reified T
: Fragment> launchFragmentInHiltContainer( fragmentArgs: Bundle? = null, crossinline action: Fragment.() -> Unit = {}, ) { val startActivityIntent = Intent.makeMainActivity(ComponentName( ApplicationProvider.getApplicationContext(), HiltTestActivity::class.java, )) ActivityScenario.launch<HiltTestActivity>(startActivityIntent).onActivity { val fragment = it.supportFragmentManager.fragmentFactory.instantiate( Preconditions.checkNotNull(T::class.java.classLoader), T::class.java.name, ) fragment.arguments = fragmentArgs it.supportFragmentManager.beginTransaction().apply { add(android.R.id.content, fragment, "") }. commitNow() fragment.action() } } https://github.com/android/architecture-samples/blob/dev- hilt/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/HiltExt.kt
@RunWith(AndroidJUnit4::class) @Config(application = HiltTestApplication::class) @HiltAndroidTest @GraphicsMode(GraphicsMode.Mode.NATIVE) class TestFragmentScreenShot { @get:Rule
var hiltRule = HiltAndroidRule(this) @Inject lateinit var xxxApi: XXXApi @Before fun setup() { hiltRule.inject() } @Test fun captureFragmentImage() { launchFragmentInHiltContainer<TestFragment> { onView(withId(R.id.testFragment)).captureRoboImage() } } } Fragment × DataBinding × Hilt で動作確認
@RunWith(AndroidJUnit4::class) @Config(application = HiltTestApplication::class) @HiltAndroidTest @GraphicsMode(GraphicsMode.Mode.NATIVE) class TestFragmentScreenShot { @get:Rule
var hiltRule = HiltAndroidRule(this) @Inject lateinit var xxxApi: XXXApi @Before fun setup() { hiltRule.inject() } @Test fun captureFragmentImage() { launchFragmentInHiltContainer<TestFragment> { onView(withId(R.id.testFragment)).captureRoboImage() } } } Fragment × DataBinding × Hilt で動作確認
@RunWith(AndroidJUnit4::class) @Config(application = HiltTestApplication::class) @HiltAndroidTest @GraphicsMode(GraphicsMode.Mode.NATIVE) class TestFragmentScreenShot { @get:Rule
var hiltRule = HiltAndroidRule(this) @Inject lateinit var xxxApi: XXXApi @Before fun setup() { hiltRule.inject() } @Test fun captureFragmentImage() { launchFragmentInHiltContainer<TestFragment> { onView(withId(R.id.testFragment)).captureRoboImage() } } } Fragment × DataBinding × Hilt で動作確認
⾊々な条件VRTできた! あとはプロジェクトに本格的に導⼊するだけ! ⼀⽇もあれば余裕だ!
…
テストが全然通らないぞ…? というかテストが終わらないぞ…?
[DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager]
Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. java.lang.NullPointerException thrown from thread "FlipperConnectionThread" [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. 以下永遠に続く……… 発⽣していたエラー
[DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager]
Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. java.lang.NullPointerException thrown from thread "FlipperConnectionThread" [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. 以下永遠に続く……… 発⽣していたエラー デッドロック的な…?
調べても全然解決できない🥺 きっとPCのスペックが⾜りないんだー! org.gradle.jvmargs=-Xmx4g gradle デーモン 再起動など ヒープ サイズ 拡⼤
よく⾒たら…
[DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager]
Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. java.lang.NullPointerException thrown from thread "FlipperConnectionThread" [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. [DefaultFileLockManager] Waiting to acquire shared lock on daemon addresse [DefaultFileLockManager] Lock acquired on daemon addresses registry. [DefaultFileLockManager] Releasing lock on daemon addresses registry. 以下永遠に続く……… 発⽣していたエラー (--debug オプションが必要) Flipper…?
・Flipperとはmy routeで使⽤しているデバッグツール ・Applicationクラスで設定 ・Applicationクラスを、Manifestファイルで指定 class MyrouteApplication : Application() { override
fun onCreate() { Flipper.initialize(this) ... } } Flipperについて <application android:name=".MyrouteApplication" /> </application>
Composeでテストを⾏うとき、起動するActivityの宣⾔があるManifestをマージする // build.gradle debugImplementation("androidx.compose.ui:ui-test-manifest:$version") <!-- app/build/merged-manifest/ --> <activity android:name="androidx.activity.ComponentActivity" android:exported="true"
/> 専⽤のActivityをManifestにマージする
// モジュールのbuild.gradle.kts android { testOptions { unitTests { isIncludeAndroidResources =
true } } } Roborazzi使うときに必要な設定 > Enables unit tests to use Android resources, assets, and manifests. https://developer.android.com/reference/tools/gradle-api/8.3/com/android/build/api/dsl/UnitTestOptions#isIncludeAndroidResou
@RunWith(AndroidJUnit4::class) @Config(application = Application::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class TestClass { } 対応⽅法
約半年後、 ようやく導⼊できました…!
教訓 ・エラー⽂はちゃんと読もう ・必要に応じて、./gradlew XXX --debug などを活⽤しよう ・何でdebugImplementationを使っているかを考えよう ・コードには理由がある(はず) ・やってないことに対して、すぐにできるでしょみたいに⾼を括らないようにしよう ・⾃分がやることにも他⼈がやることに対しても
ご清聴ありがとうございました!