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
Android Architecture Componentとテスト2019年7月版 / Te...
Search
tkmnzm
July 25, 2019
Programming
1.2k
8
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Android Architecture Componentとテスト2019年7月版 / Testing Android Architecture Components in 2019-07
tkmnzm
July 25, 2019
More Decks by tkmnzm
See All by tkmnzm
AndroidアプリのUIバリエーションをあの手この手で確認する / Check UI variations of Android apps by various means
tkmnzm
1
1.7k
Androidアプリの良いユニットテストを考える / Thinking about good unit tests for Android apps
tkmnzm
5
10k
Google I:O 2023 Androidの自動テストアップデートまとめ / Google I:O 2023 Android Testing Update Recap
tkmnzm
0
720
コルーチンのエラーをテストするためのTips / Tips for testing Kotlin Coroutine errors
tkmnzm
0
1.3k
Androidのモダンな技術選択にあわせて自動テストも アップデートしよう / Update your automated tests to match Android's modern technology choices
tkmnzm
3
2.6k
SWET dev-vitalチームによるプロジェクトの健康状態可視化の取り組み / SWET dev-vital team's efforts to visualize the health of the project
tkmnzm
1
1.4k
モバイルアプリテスト入門 / Getting Started with Mobile App Testing
tkmnzm
1
660
25分で作るAndroid Lint / Android Lint made in 25 minutes
tkmnzm
0
1.1k
2年半ぶりのプロダクト開発であらためて感じた自動テストの大切さ / realized the importance of automatic testing with product development for the first time in two and a half years
tkmnzm
1
890
Other Decks in Programming
See All in Programming
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
8
5.5k
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
440
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
480
すこし踏み込む CancellationToken
htkym
2
1.6k
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
160
Workers Cache を知る
syumai
0
330
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
1
2.2k
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
150
ジョジョの世界をXRで再現する
mofurune
0
110
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
13
6.5k
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
360
Featured
See All Featured
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
860
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.7k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
700
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
250
Between Models and Reality
mayunak
4
480
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
510
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
490
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
490
Transcript
Architecture Componentとテストのまとめ [2019年7月版] DeNA SWETグループ Nozomi Takuma
自己紹介 Nozomi Takum DeNA SWETグループ Androidとテストが好き
Architecture Componentとテスト 日々進化するArchitecture Component テスト周りも同じくアップデートされる 現時点(2019年7月)での各componentのテストに関するトピックを 整理
本日登場するComponent ・ LiveData ・ ViewModel ・ Room ・ WorkManager ・
Navigation
LiveData Observe可能なデータホルダー Androidのライフサイクル(Activity/Fragment/Service)を意識して動 作 ViewModelやRoomなどその他のArchitecture componentと連携し て使われることが多い
LiveDataのテスト ・ ObserveForever ・ InstantTaskExecutorRule
ObserveForever LiveDataをobserveするときのメソッドは2種類 observe(lifecycleOwner, observer) observeFroever(observer) テストのときはLifecycleOwnerのインスタンスが不要な observeFroeverを使うのが楽
ObserveForever // Observer にどんな値が渡ってくるかを記録するためのspy val spyObserver = spyk<Observer<String>>() spy をobserveForever
viewmodel.strLiveData.observeForever(spyObserver) // strLiveData をTest にする実装 viewmodel.changeText("Test") verify { // onChanged の引数をVerify で検証 spyObserver.onChanged("test") }
InstantTaskExecutorRule 先程のテストコードをJunitで実行するとエラーになる java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. InstantTaskExecutorRuleを使用する
InstantTaskExecutorRule background executorを差し替えて同期的に実行してくれる android.arch.core:core‑testing class LiveDataTest { @get:Rule var instantExecutorRule
= InstantTaskExecutorRule() }
InstantTaskExecutorRule とてもわかりやすい解説 https://medium.com/@star_zero/livedataのunittest‑ 2b295d2818c1
ViewModel UIにひもづくデータの保持と管理を行う 画面のライフサイクルを意識して動作 ドメイン層・モデル層へのエンドポイントとして使用されることが 多い
ViewModelのテスト ・ ViewModelScope ・ SavedStateHandler
典型的なViewModelのテスト class TaskViewModelTest { @get:Rule var instantExecutorRule = InstantTaskExecutorRule() @Test
fun test() { val taskRepositoty = mock/fake TaskRepository val viewModel = TaskViewModel(taskRepositoty) val spyObserver = spyk<Observer<String>>() viewmodel.notifyText.observeForever(spyObserver) viewmodel.addTask("Test") verify { spyObserver.onChanged("add task:Test !") } } }
ViewModelScope ViewModelがDestroyするときに自動でcoroutineのキャンセルをし てくれる ViewModelのextension propertyとして定義 デフォルトはDispatchers.MainがCoroutine Dispatherとして使われ ている
ViewModelScope class TaskViewModel() : ViewModel() { fun addTask(taskName: String) {
viewModelScope.launch { .. } } }
Testing with ViewModelScope Dispathers.Mainをテスト用のCoroutineDispatherに差し替える org.jetbrains.kotlinx:kotlinx‑coroutines‑test val testDispatcher = TestCoroutineDispatcher() @Before
fun setup() { Dispatchers.setMain(testDispatcher) } @After fun teardown() { Dispatchers.resetMain() }
Testing with ViewModelScope Ruleとして定義しておくと便利 ref: https://bit.ly/2K10suX
Testing with ViewModelScope ViewModelScope内でdelayがあるときは、delayした分の時間をすすめ て処理を再開してあげる val testDispatcher = TestCoroutineDispatcher() ..
@Test fun testDelay() { val viewModel = TaskViewModel() // ViewModelScope 内でdelay(1_000) viewModel.delayMathod() // TestCoroutineDispatcher#advanceTimeBy // advanceUntilIdle() というのもある testDispatcher.advanceTimeBy(1_000) // assertion }
SavedStateHandler ViewModelからsavedInstanceStateへのアクセスが可能になる androidx.lifecycle:lifecycle‑viewmodel‑savedstate
SavedStateHandler class MyViewModel(val savedState: SavedStateHandle) : ViewModel() { val text
= savedStateHandle.getLiveData<String>("text") fun updateText() { savedStateHandle.set("text", "test") } }
SavedStateHandler class SavedStateViewModelTest { @get:Rule val testRule = InstantTaskExecutorRule() @Test
fun test() { // 空のSavedStateHandle を渡す val target = SavedStateViewModel(SavedStateHandle()) target.updateText() val spyObserver = spyk<Observer<String>>() target.text.observeForever(spyObserver) verify(spyObserver).onChanged("test") } }
Room SQLiteへのアクセスを抽象化 アノテーションから実装を生成 LiveDataでのテーブル監視、Rxやcoroutineのサポート、ビュー機 能など高機能
Roomのテスト ・ Dao Interface ・ in‑memory database ・ Robolectric support
Dao Interface RoomではDaoをinterfaceで定義する それによりDaoを使用するクラスはRoomの実装をテストダブルで 置き換えるのが容易 モックで置き換えたり、後述のin‑memory databaseと組み合わせ たりが可能
Dao Interface class TasksRepository (val tasksDao: TasksDao) val db =
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "Tasks.db").build() val repo = TasksRepository(db.tasksDao()) モックへの置き換え val mock = mockk<Taskdao>() val repo = TasksRepository(mock)
in‑memory database メモリ上にデータをstoreしてくれる プロセスが消えたらデータも消える Fakeの用途でテストに使うことが可能
in‑memory database @RunWith(AndroidJUnit4::class) class TaskDaoTest { private lateinit var db:
TestDatabase @Before fun setUp() { val context = ApplicationProvider .getApplicationContext<Context>() db = Room.inMemoryDatabaseBuilder( context, TestDatabase::class.java).build() } @Test fun test() { // Room のin‑memory database のDAO 実装を注入 val repo = TasksRepository(db.tasksDao()) } @After fun tearDown() { db.close() }
Robolectric support RoomのテストはLocal Testでも動作する ただLocalでのテストは推奨ではない https://developer.android.com/training/data‑ storage/room/testing‑db#host‑machine InstrumentationTestでもLocal動作するようにテストファイルを配 置してあげるのがよさそう
WorkManager バッググラウンドタスクの実行・管理を行う 延期可能&必ず実行したいバックグラウンドタスクを実装するとき に使用 バックグラウンド関連APIの差分を意識しなくてよくなった
WorkManagerのテスト ・ WorkerBuilder(WorkManager 2.1.0+) ・ WorkerFactor ・ Robolectric Support
TestListenableWorkerBuilder val context = ApplicationProvider.getApplicationContext() val worker = TestListenableWorkerBuilder<MyWorker>( inputData
= workDataOf("key" to "value") context = context).build() val result = worker.startWork().get() assertThat(result, equalTo(Result.success()))
TestWorkerBuilder Executorが差し替えられるのがTestListenableWorkerBuilderとの違い val worker = TestWorkerBuilder<MyWorker>( context = context, executor
= executor, inputData = workDataOf("key" to "value")).build() val result = worker.startWork().get() assertThat(result, equalTo(Result.success()))
WorkerFactory 通信処理だったりDBへのアクセスだったりにテストにしにくい要素 に依存することがある WorkerFactoryを利用することで任意のコンストラクタを持つ WorkManagerを作成可能
WorkerFactory 通常のWorkManager val request = OneTimeWorkRequestBuilder<MyWorker>().build() WorkManager.getInstance(context).enqueue(request) class MyWorker( context:
Context, workerParams: WorkerParameters)
WorkerFactory class MyWorkerFactory : WorkerFactory() { override fun createWorker( appContext:
context, workerClassName: name, workerParameters: params) : ListenableWorker? { if (name == MyWorker::class.java.name) { // MyWorker の依存クラスを追加 return MyWorker(context, params, MyDependency()) } return null } }
WorkerFactory Custom Appliction class MyApp : Application() { override fun
onCreate() { super.onCreate() val config = Configuration.Builder() .setWorkerFactory(MyWorkerFactory()).build() WorkManager.initialize(this, config) } } AndroidManifest.xmlでデフォルトのInitializerをremoveする <provider android:name="androidx.work.impl.WorkManagerInitializer" android:authorities="${applicationId}.workmanager‑init" tools:node="remove" />
WorkerFactory @Before fun setUp() { val context = ApplicationProvider.getApplicationContext() val
config = Configuration.Builder() .setWorkerFactory(TestMyWorkerFactory()) .build() WorkManagerTestInitHelper. initializeTestWorkManager(context, config) }
Robolectric Support WorkManagerのテストはLocal Testでも動作する Issueでサポートしない旨の回答があったが、無事に対応された模様 WorkManagerの初期化をしないと実行できなかった @Before fun setUp() {
val context = ApplicationProvider.getApplicationContext() // WorkManagerTestInitHelper をつかう // WorkManager.initialize(context, config) は // Instrumentation Test で実行するとエラーになる WorkManagerTestInitHelper. initializeTestWorkManager(context, config) }
Navigation 画面の遷移の実装をサポート シンプルな遷移から、AppBarやNavigationDrawerとの連携など 画面の遷移をNavigation graphで記述する(フォーマットはXML)
Navigationのテスト ・ with Fragment ・ with Activity
with Fragment FragmentScenarioを使用し、Fragmentにmockの NavigationControllerをセットする Fragmentから別の画面に遷移したときに、NavigationControllerに 正しい引数が渡されているかを見る
with Fragment val navController = mockk<NavController>(relaxed = true) val scenario
= launchFragmentInContainer<TaskFragment>() scenario.onFragment { Navigation.setViewNavController(it.view!!, navController) } ... verify { navController.navigate(TaskFragmentDirections .actionTaskFragmentToTaskListFragment()) }
with Activity 基本的には遷移した後の画面のUIを検証すればいいので、 Navigationを意識する必要ない 内部的にNavigationを使ったActivityScenarioのテストは Robolectric上でも動作する
本日紹介したComponent ・ LiveData ・ ViewModel ・ Room ・ WorkManager ・
Navigation
Summary Architecture Componentはテストのサポートが充実している アップデートにより、書けなかったテストが書けるようになること もある Architecture Component自体の機能も日々アップデートしているの で、あわせてテストはどうなる?も気にかけたい
ご清聴ありがとうございました