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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
MySQL, PostgreSQLのオブザーバビリティをGrafanaで向上させた話
mot_techtalk
2
510
タクシー車両の位置状態情報を支えるシステムのインフラ構成
mot_techtalk
2
550
SREの視点で挑む Redis Stream 無停止移行
mot_techtalk
2
520
生成AIで社内データを整備しよう
mot_techtalk
1
240
GO Tech Talk #31 タクシーアプリ『GO』におけるNext.jsの活用
mot_techtalk
2
560
大規模基幹サーバーに gRPCを導入した過程での学び
mot_techtalk
2
61
大規模アプリのDIフレームワーク刷新戦略 ~過去最大規模の並行開発を止めずにアプリ全体に導入するまで~
mot_techtalk
1
590
GPT-5と寿司合戦を攻略する
mot_techtalk
1
150
Grafanaスタックをフル活用したオブザーバビリティ基盤の紹介
mot_techtalk
7
1.5k
Other Decks in Programming
See All in Programming
Rで始めるML・LLM活用入門
wakamatsu_takumu
0
170
AHC061解説
shun_pi
0
350
コーディングルールの鮮度を保ちたい / keep-fresh-go-internal-conventions
handlename
0
180
Ruby x Terminal
a_matsuda
7
590
AI主導でFastAPIのWebサービスを作るときに 人間が構造化すべき境界線
okajun35
0
680
CDIの誤解しがちな仕様とその対処TIPS
futokiyo
0
200
Cyrius ーLinux非依存にコンテナをネイティブ実行する専用OSー
n4mlz
0
120
PostgreSQL を使った快適な go test 環境を求めて
otakakot
0
530
PJのドキュメントを全部Git管理にしたら、一番喜んだのはAIだった
nanaism
0
250
RubyとGoでゼロから作る証券システム: 高信頼性が求められるシステムのコードの外側にある設計と運用のリアル
free_world21
0
260
AI時代でも変わらない技術コミュニティの力~10年続く“ゆるい”つながりが生み出す価値
n_takehata
2
700
エラーログのマスキングの仕組みづくりに役立ったASTの話
kumoichi
0
160
Featured
See All Featured
Evolving SEO for Evolving Search Engines
ryanjones
0
150
Exploring anti-patterns in Rails
aemeredith
2
290
Agile that works and the tools we love
rasmusluckow
331
21k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
0
230
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
230
The Art of Programming - Codeland 2020
erikaheidi
57
14k
The browser strikes back
jonoalderson
0
770
GraphQLとの向き合い方2022年版
quramy
50
14k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
190
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
110
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
22k
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.