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
520
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
そのAPI、誰のため? Androidライブラリ設計における利用者目線の実践テクニック
mkeeda
2
5.4k
Android StudioのAIコーディングツール、 ぶっちゃけどうなん???
mkeeda
0
390
時計仕掛けのCompose
mkeeda
1
480
What's new in Firebase for building gen AI features気になったところ
mkeeda
0
840
手動DIの教訓
mkeeda
0
250
WebViewと向き合う
mkeeda
2
1.5k
お気に入りのAndroid Studio小技集
mkeeda
0
380
Scalable UI testing solutions かんたんまとめ
mkeeda
0
1.3k
5分で分かるビルドロジック共通化の今
mkeeda
1
1.7k
Other Decks in Programming
See All in Programming
FOSDEM 2026: STUNMESH-go: Building P2P WireGuard Mesh Without Self-Hosted Infrastructure
tjjh89017
0
150
CSC307 Lecture 09
javiergs
PRO
1
830
OSSとなったswift-buildで Xcodeのビルドを差し替えられるため 自分でXcodeを直せる時代になっている ダイアモンド問題編
yimajo
3
610
16年目のピクシブ百科事典を支える最新の技術基盤 / The Modern Tech Stack Powering Pixiv Encyclopedia in its 16th Year
ahuglajbclajep
5
990
2026年 エンジニアリング自己学習法
yumechi
0
130
カスタマーサクセス業務を変革したヘルススコアの実現と学び
_hummer0724
0
650
副作用をどこに置くか問題:オブジェクト指向で整理する設計判断ツリー
koxya
1
590
MDN Web Docs に日本語翻訳でコントリビュート
ohmori_yusuke
0
640
今から始めるClaude Code超入門
448jp
7
8.5k
AIエージェント、”どう作るか”で差は出るか? / AI Agents: Does the "How" Make a Difference?
rkaga
4
2k
プロダクトオーナーから見たSOC2 _SOC2ゆるミートアップ#2
kekekenta
0
200
React 19でつくる「気持ちいいUI」- 楽観的UIのすすめ
himorishige
11
6k
Featured
See All Featured
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
BBQ
matthewcrist
89
10k
Docker and Python
trallard
47
3.7k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.3k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
0
270
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.9k
Building a Scalable Design System with Sketch
lauravandoore
463
34k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
9.5k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
96
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Building an army of robots
kneath
306
46k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.2k
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