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
DataStoreをテストする
Search
mkeeda
April 04, 2025
Programming
0
440
DataStoreをテストする
Shibuya.apk #52の発表資料です
https://shibuya-apk.connpass.com/event/349013/
mkeeda
April 04, 2025
Tweet
Share
More Decks by mkeeda
See All by mkeeda
Android StudioのAIコーディングツール、 ぶっちゃけどうなん???
mkeeda
0
8
時計仕掛けのCompose
mkeeda
1
410
What's new in Firebase for building gen AI features気になったところ
mkeeda
0
740
手動DIの教訓
mkeeda
0
210
WebViewと向き合う
mkeeda
2
1.4k
お気に入りのAndroid Studio小技集
mkeeda
0
350
Scalable UI testing solutions かんたんまとめ
mkeeda
0
1.2k
5分で分かるビルドロジック共通化の今
mkeeda
1
1.6k
Compose で手に入れた UI の Unit test
mkeeda
3
2k
Other Decks in Programming
See All in Programming
ProxyによるWindow間RPC機構の構築
syumai
3
1k
CSC305 Summer Lecture 12
javiergs
PRO
0
130
もうちょっといいRubyプロファイラを作りたい (2025)
osyoyu
0
290
【第4回】関東Kaggler会「Kaggleは執筆に役立つ」
mipypf
0
1k
テストカバレッジ100%を10年続けて得られた学びと品質
mottyzzz
2
480
Claude Codeで実装以外の開発フロー、どこまで自動化できるか?失敗と成功
ndadayo
4
1.9k
Vue・React マルチプロダクト開発を支える Vite
andpad
0
110
実用的なGOCACHEPROG実装をするために / golang.tokyo #40
mazrean
1
220
testingを眺める
matumoto
1
130
オープンセミナー2025@広島LT技術ブログを続けるには
satoshi256kbyte
0
160
レガシープロジェクトで最大限AIの恩恵を受けられるようClaude Codeを利用する
tk1351
4
1.6k
FindyにおけるTakumi活用と脆弱性管理のこれから
rvirus0817
0
420
Featured
See All Featured
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
33
2.4k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
187
54k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
1.5k
The Straight Up "How To Draw Better" Workshop
denniskardys
236
140k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3.1k
Unsuck your backbone
ammeep
671
58k
RailsConf 2023
tenderlove
30
1.2k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
30
9.6k
Building a Modern Day E-commerce SEO Strategy
aleyda
43
7.5k
Visualization
eitanlees
148
16k
Transcript
DataStoreをテストする Shibuya.apk # 5 2 向井 田 一 平 (@mr_mkeeda)
• 向井 田 一 平 (むかいだ いっぺい) • 𝕏 :
@mr_mkeeda • Github: @mkeeda • Android Engineer at Cybozu, Inc • 最近ハマっているもの: ガンニバル🍖 2
DataStore “を” テストする • androidx.DataStore • DataStoreの処理 自 体をテストする •
ex. 独 自 実装のSerializerを使ったデータの読み書きをテスト • DataStoreを含めたテスト対象をテストする • ex. DataStoreをモックせずにRepositoryの実装をテスト 3
DataStoreテストの流れ 1 . DataStoreインスタンスを作成 • CoroutineDispatcherの差し替え ⭐ 2 . 前提の状況を作る
• 事前にデータを書き込んでおく • 別のテストケースで書き込んだデータの削除 ⭐ 3 . テストしたい処理を実 行 4 . 期待する振る舞いを検証 • データ書き込みしたファイルの中 身 を検証 ⭐ 4
CoroutineDispatcherの変え 方 5 val Context.testDataStore by dataStore( fileName = "test_file",
serializer = StringSerializer(defaultValue = "default"), scope = CoroutineScope(UnconfinedTestDispatcher() + Job()) // ⭐ ) // or val testDataStore = DataStoreFactory.create( serializer = StringSerializer(defaultValue = "default"), scope = CoroutineScope(UnconfinedTestDispatcher() + Job()), // ⭐ produceFile = { context.dataStoreFile(fileName = "test_file") } )
データの後始末 6 internal fun Context.clearDataStore() { File(filesDir, "/datastore").deleteRecursively() } @Before
fun setUp() { context.clearDataStore() } // or val testDataStore = DataStoreFactory.create( serializer = StringSerializer(defaultValue = "default"), produceFile = { /** Ұ࣌ϑΝΠϧΛ࡞Δ **/ } )
書き込まれたファイルの検証 方 法 7 val testDataStore = DataStoreFactory.create( serializer =
StringSerializer("default"), produceFile = { context.dataStoreFile(fileName = "test_file") } ) testDataStore.updateData { "Hello world" } // ⭐ val storedFile = context.filesDir.findFileBy("test_file") storedFile ?. readText() .shouldBe("Hello world") .shouldNotBe("default")
8 internal fun File.findFileBy(fileName: String): File? = searchFileRecursively(directory = this,
fileName) private fun searchFileRecursively( directory: File, fileName: String, ): File? { directory.listFiles() ?. forEach { file -> if (file.isDirectory) { searchFileRecursively(file, fileName) ?. let { return it } } else if (file.name.contains(fileName)) { return file } } return null }
ファイル参照の競合 • DataStoreインスタンスは シングルトンで使う設計 • 同 一 のファイル名を 同時に参照するとエラー •
AndroidTestだと1プロセス内で 同 一 参照があるとエラー • 複数のテストを実 行 しないと エラーに気付けない! 9 DataStore ファイル class TestA class TestB fun test 1 () fun test 2 () fun test 3 () ⚠ ⚠ ⚠
ファイル参照の競合を回避する • ファイル名をテストケース単位で ユニークにする • JUnitのRuleを作るといいかも (試してない) 1 0 DataStore
TestA_test 1 _ fi le class TestA class TestB fun test 1 () fun test 2 () fun test 3 () DataStore TestA_test 2 _ fi le DataStore TestB_test 3 _ fi le
ファイルの参照を解放させたい • DataStoreは稼働中、書き込み先のファイルを占有する • 2番 目 に参照してきたDataStoreはエラーになる • では、こういうのはどうやってテストする? 1
. DataStoreAでデータを書き込み 2 . アプリ再起動などでDataStoreAが破棄 3 . 1で書き込んだデータをDataStoreBで読み込める 1 1
1 2 val testDataStore1 = DataStoreFactory.create( produceFile = { context.dataStoreFile(fileName
= "test_file") } ) testDataStore1.updateData { "Hello world" } // ΞϓϦϓϩηεΛऴྃͤ͞Δ // ʁʁʁ⭐ val testDataStore2 = DataStoreFactory.create( produceFile = { context.dataStoreFile(fileName = "test_file") } ) // ॻ͖ࠐΜͩσʔλ͕ಡΈࠐΊΔ͜ͱΛ֬ೝ testDataStore2.data.first() shouldBe "Hello world"
Jobを完了させるとDataStoreはファイル参照を解放する 1 3 val dataStoreJob = Job() val testDataStore1 =
DataStoreFactory.create( scope = CoroutineScope( UnconfinedTestDispatcher() + dataStoreJob // ⭐ ), produceFile = { context.dataStoreFile(fileName = "test_file") } ) dataStoreJob.complete()
DataStoreテストまとめ • 基本編 • CoroutineDispatcherはscopeを差し替える • テスト後の後始末はファイルごと消す • DataStoreの書き込み挙動はファイルの中 身
を検証する • 上級編 • ファイル参照の競合に気をつける • DataStoreのファイル参照を解放するには scopeに含めたJobを完了させる 1 4
参考 • DataStore and testing | Android Developers | Medium
https://medium.com/androiddevelopers/datastore-and-testing- edf 7 ae 8 df 3 d 8 • androidx.DataStoreのテストコード https://cs.android.com/androidx/platform/frameworks/support/+/ androidx-main:datastore/ 1 5