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
生成AIで社内データを整備しよう
mot_techtalk
1
210
GO Tech Talk #31 タクシーアプリ『GO』におけるNext.jsの活用
mot_techtalk
2
460
大規模基幹サーバーに gRPCを導入した過程での学び
mot_techtalk
2
41
大規模アプリのDIフレームワーク刷新戦略 ~過去最大規模の並行開発を止めずにアプリ全体に導入するまで~
mot_techtalk
1
560
GPT-5と寿司合戦を攻略する
mot_techtalk
1
120
Grafanaスタックをフル活用したオブザーバビリティ基盤の紹介
mot_techtalk
7
1.3k
オンデマンド交通のための車両ルーティング問題
mot_techtalk
0
170
Open-Vocabularyオブジェクト検出
mot_techtalk
0
410
Grafana Loki によるサーバログのコスト削減
mot_techtalk
1
1k
Other Decks in Programming
See All in Programming
Denoのセキュリティに関する仕組みの紹介 (toranoana.deno #23)
uki00a
0
220
TestingOsaka6_Ozono
o3
0
260
「コードは上から下へ読むのが一番」と思った時に、思い出してほしい話
panda728
PRO
39
26k
Grafana:建立系統全知視角的捷徑
blueswen
0
270
ゲームの物理 剛体編
fadis
0
390
Kotlin Multiplatform Meetup - Compose Multiplatform 외부 의존성 아키텍처 설계부터 운영까지
wisemuji
0
160
生成AI時代を勝ち抜くエンジニア組織マネジメント
coconala_engineer
0
37k
AtCoder Conference 2025「LLM時代のAHC」
imjk
2
640
Pythonではじめるオープンデータ分析〜書籍の紹介と書籍で紹介しきれなかった事例の紹介〜
welliving
3
760
[AtCoder Conference 2025] LLMを使った業務AHCの上⼿な解き⽅
terryu16
6
1k
Vibe codingでおすすめの言語と開発手法
uyuki234
0
160
ELYZA_Findy AI Engineering Summit登壇資料_AIコーディング時代に「ちゃんと」やること_toB LLMプロダクト開発舞台裏_20251216
elyza
2
940
Featured
See All Featured
What does AI have to do with Human Rights?
axbom
PRO
0
1.9k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
1.9k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
41
Designing Experiences People Love
moore
143
24k
Code Review Best Practice
trishagee
74
19k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
66
Building an army of robots
kneath
306
46k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
1
270
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
29
Producing Creativity
orderedlist
PRO
348
40k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
78
Exploring anti-patterns in Rails
aemeredith
2
220
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.