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
iOSアプリ開発における効果的なUnitTestの戦略とその実装
Search
GO Inc. dev
September 28, 2023
Programming
1
1.1k
iOSアプリ開発における効果的なUnitTestの戦略とその実装
後夜祭 iOSDC Japan 2023
で発表した資料です。
GO Inc. dev
September 28, 2023
Tweet
Share
More Decks by GO Inc. dev
See All by GO Inc. dev
Open-Vocabularyオブジェクト検出
mot_techtalk
0
7
Grafana Loki によるサーバログのコスト削減
mot_techtalk
1
680
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
680
GAEログのコスト削減
mot_techtalk
0
650
『GO』アプリ バックエンドサーバのコスト削減
mot_techtalk
0
690
タクシーアプリ『GO』のリアルタイムデータ分析基盤における機械学習サービスの活用
mot_techtalk
6
3.2k
App Store Connect APIで 作業時間を増やそう
mot_techtalk
4
680
Streamlitで結合テスト工数の削減に成功した話
mot_techtalk
8
3.3k
Streamlit ホスティング基盤をどのように作ったか
mot_techtalk
4
1.9k
Other Decks in Programming
See All in Programming
Beyond Portability: Live Migration for Evolving WebAssembly Workloads
chikuwait
0
400
AIプログラマーDevinは PHPerの夢を見るか?
shinyasaita
1
170
Google Agent Development Kit でLINE Botを作ってみた
ymd65536
2
200
Goで作る、開発・CI環境
sin392
0
140
deno-redisの紹介とJSRパッケージの運用について (toranoana.deno #21)
uki00a
0
150
設計やレビューに悩んでいるPHPerに贈る、クリーンなオブジェクト設計の指針たち
panda_program
6
1.6k
ReadMoreTextView
fornewid
1
480
Systèmes distribués, pour le meilleur et pour le pire - BreizhCamp 2025 - Conférence
slecache
0
110
なんとなくわかった気になるブロックテーマ入門/contents.nagoya 2025 6.28
chiilog
1
230
プロダクト志向ってなんなんだろうね
righttouch
PRO
0
170
すべてのコンテキストを、 ユーザー価値に変える
applism118
2
920
PostgreSQLのRow Level SecurityをPHPのORMで扱う Eloquent vs Doctrine #phpcon #track2
77web
2
390
Featured
See All Featured
KATA
mclloyd
30
14k
The World Runs on Bad Software
bkeepers
PRO
69
11k
Making the Leap to Tech Lead
cromwellryan
134
9.4k
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.4k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
17
940
For a Future-Friendly Web
brad_frost
179
9.8k
GitHub's CSS Performance
jonrohan
1031
460k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.6k
Making Projects Easy
brettharned
116
6.3k
Transcript
© GO Inc. iOSアプリ開発における効果的 なUnitTestの戦略とその実装 2023.09.26 開発本部 ソフトウェア開発統括部 ユーザーシステム開発部 ユーザーシステム1グループ
/ 髙橋秀宗 GO株式会社
© GO Inc. 2 自己紹介 GO株式会社 ユーザーシステム1グループ / 髙橋秀宗 2022年12月に入社。タクシーアプリ『GO』のiOSアプリ
開発を担当 趣味はエレキギター演奏 お気に入りのアンプはMarshall JCM800 @h1d3mun3
Index © GO Inc. 1. UnitTestとは 2. 単一責任原則 3. 実践UnitTest
4. まとめ 5. 参考資料 3
© GO Inc. UnitTestとは 01
© GO Inc. V字モデル 5 実装 基本設計 結合テスト 詳細設計 コンポーネントテスト
(単体テスト ・UnitTest) 要件定義 システム/受け入れ テスト 対応するテストスコープ https://shiftasia.com/ja/column/v%E5%AD%97%E3%83%A2%E3%83%87%E3%83%AB%E3%81%A8%E3%81%AF/
© GO Inc. V字モデル 6 実装 基本設計 結合テスト 詳細設計 コンポーネントテスト
(単体テスト ・UnitTest) 要件定義 システム/受け入れ テスト 対応するテストスコープ https://shiftasia.com/ja/column/v%E5%AD%97%E3%83%A2%E3%83%87%E3%83%AB%E3%81%A8%E3%81%AF/
© GO Inc. • 詳細設計で実装したものをテストする ◦ 作ったクラスや、変更した処理など • 要件定義・基本設計レベルの観点はテストしない UnitTestのスコープ
7
© GO Inc. • ホワイトボックステスト ◦ システムの内部も考慮したテスト ◦ if式やswitch式、guard式などが動くかどうかも考慮する •
ブラックボックステスト ◦ システムの外部をテストする ◦ 入力に対して期待する出力かどうかを考慮する ホワイトボックステスト・ブラックボックステスト 8
© GO Inc. • 🙆 メリット 🙆 ◦ 高速に、何度も、正確に実施できる ◦
実施コストが低い • 🙅 デメリット🙅 ◦ 状況に応じて操作を分けるのが難しい ◦ 人間的な操作など、非規則的なテストが難しい UnitTestの特徴 9
© GO Inc. • テスト対象 ◦ 詳細設計に対応するので、実装したクラスや処理などをチェック • コスト ◦
実行コストが低いので、CI上でDailyで回すなどして、問題の早期発見に活 用する • テスト方式 ◦ 問題の早期発見を主眼にするので、クラスや処理の入力と出力を見るブラッ クボックステストを行う UnitTestの効果的な使い方 10
© GO Inc. 単一責任原則 02
© GO Inc. • モジュールはたったひとつのアクターに対して責務を負うべきである、という原 則 • アクターとは「ユーザーやステークホルダーのグループ」のこと ◦ もちろんソースコード内部の別ファイルもアクターになりうる
🎉 → これが守られていると、モジュール(≒ソースコード)を変更する理由は必ず1つに なる 単一責任原則 12
© GO Inc. 実践UnitTest 03
© GO Inc. 1. 既存のコードをアクターを単位にして分割する 2. 分割したコードの振る舞いをProtocolに抽象化しDI可能にする 3. それぞれに対してMockを利用してテストをする 既存のコードにUnitTestを適用させる
14
© GO Inc. やってみよう! • Notification構造体でアプリ内お知らせを表現 • NotificationManagerクラスが、アプリ内お知らせを管理 し、下記を行っている ◦
現在のお知らせ一覧を返却する機能 ◦ メッセージの既読処理 ◦ APIを経由したサーバーへの反映処理 15
© GO Inc. Notification構造体 struct Notification { let title: String
let isRead: Bool } 16
© GO Inc. NotificationManagerクラス class NotificationManager { func getAllUnReadNotifications() ->
[Notification] { 〜〜〜 } func markAsRead(notification: Notification, completion: @escaping (() -> Void)) { 〜〜〜 } func postApi(notification: Notification, completion: @escaping (() -> Void)) { 〜〜〜 } } 17
© GO Inc. 修正前クラス図 18
© GO Inc. 1. 既存のコードをアクターを単位にして分割する 2. 分割したコードの振る舞いをProtocolに抽象化しDI可能にする 3. それぞれに対してMockを利用してテストをする 再掲:
既存のコードにUnitTestを適用させる 19
© GO Inc. 各責務をそれぞれ別々のファイルに分割する • メッセージの既読処理 ◦ → MarkAsReadUseCase •
現在のお知らせ一覧を返却する機能 ◦ → NotificationRepository • APIを経由したサーバーへの反映処理 ◦ → NotificationServerDataStore 既存のコードをアクターを単位にして分割する 20
© GO Inc. 修正後クラス図 21
© GO Inc. 1. 既存のコードをアクターを単位にして分割する 2. 分割したコードの振る舞いをProtocolに抽象化しDI可能にする 3. それぞれに対してMockを利用してテストをする 再掲:
既存のコードにUnitTestを適用させる 22
© GO Inc. UnitTestのときにMockを作成しやすくするため、責務をProtocolに抽象化する 分割したコードの振る舞いをProtocolに抽象化しDI可能にする 23
© GO Inc. 修正後クラス図 24
© GO Inc. 1. 既存のコードをアクターを単位にして分割する 2. 分割したコードの振る舞いをProtocolに抽象化しDI可能にする 3. それぞれに対してMockを利用してテストをする 再掲:
既存のコードにUnitTestを適用させる 25
© GO Inc. UnitTestを書く 〜 MarkAsReadUseCaseImpl 26 protocol MarkAsReadUseCase {
func execute(notification: Notification, completion: @escaping (() -> Void)) } struct MarkAsReadUseCaseImpl: MarkAsReadUseCase { let notificationRepository: NotificationRepository func execute(notification: Notification, completion: @escaping (() -> Void)) { notificationRepository.store(notification: readNotification, completion: completion) } }
© GO Inc. UnitTestを書く 〜 MarkAsReadUseCaseImpl 27 MarkAsReadUseCaseImplの役割は1つ 1. RepositoryのStore関数を呼び出す
→ 上記が正しく動作することをUnitTestで確認する
© GO Inc. UnitTestを書く 〜 MarkAsReadUseCaseImpl 28 1. NotificationRepositoryのMockを作る 2.
MockをDIしてMarkAsReadUseCaseImplを初期化する 3. テスト用のNotificationを利用してmarkAsRead関数を呼び出す。 4. Repositoryの関数呼び出し回数と、取り扱っているデータが正しいことを確認 する
© GO Inc. NotificationRepositoryのMockを作る 29 struct FakeNotificationRepository: NotificationRepository { func
getAllNotification() -> [Notification] { return [] } var storeCallCount = 0 var storeArguments: Notification! func store(notification: Notification, completion: @escaping (() -> Void)) { storeCallCount += 1 storeArguments = notification completion() } }
© GO Inc. let fakeRepository = FakeNotificationRepository() execute関数のテストを書く 30
© GO Inc. let fakeRepository = FakeNotificationRepository() let subject =
MarkAsReadUseCaseImpl(notificationRepository: fakeRepository) execute関数のテストを書く 31
© GO Inc. let fakeRepository = FakeNotificationRepository() let subject =
MarkAsReadUseCaseImpl(notificationRepository: fakeRepository) let fakeNotification = Notification(title: "テストお知らせ", isRead: false) subject.execute(notification: fakeNotification, completion: { in }) execute関数のテストを書く 32
© GO Inc. let fakeRepository = FakeNotificationRepository() let subject =
MarkAsReadUseCaseImpl(notificationRepository: fakeRepository) let fakeNotification = Notification(title: "テストお知らせ", isRead: false) subject.execute(notification: fakeNotification, completion: { in }) expect(fakeRepository.storeCallCount).to(equal(1)) expect(fakeRepository.storeArguments).to(equal(fakeNotification)) execute関数のテストを書く 33
© GO Inc. UnitTestを書いたことによる副次的なメリット • アクターをベースとした責務で切り分けたので、既存コードに対して修正を行っ た際の意図せぬ副作用のリスクを低減できた 34
© GO Inc. まとめ 04
© GO Inc. まとめ 36 • UnitTestは詳細設計に対応するテストで、クラスや処理をテストする • UnitTestをCI上でDailyで回すなどして問題の早期発見に活かすと良い •
既存コードに適用する場合は、アクターという観点で既存コードを分解してから UnitTestを組む
© GO Inc. 参考資料 05
© GO Inc. まとめ 38 • V字モデルとは ◦ https://shiftasia.com/ja/column/v%E5%AD%97%E3%83%A2%E3%83 %87%E3%83%AB%E3%81%A8%E3%81%AF/
• ホワイトボックステストとは ◦ https://service.shiftinc.jp/column/4801/ • solid+cqs+dry ◦ https://speakerdeck.com/kgmyshin/solid-plus-cqs-plus-dry
文章・画像等の内容の無断転載及び複製等の行為はご遠慮ください。 © GO Inc.