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
WACATE2019_summer_BPP
Search
Daiki Katayama
June 15, 2019
Programming
1
2.3k
WACATE2019_summer_BPP
WACATE2019 夏のBPPセッションスライドです。
Daiki Katayama
June 15, 2019
Tweet
Share
More Decks by Daiki Katayama
See All by Daiki Katayama
iOSアプリのパフォーマンス計測をおさらいする
kariad
2
9.2k
アプリのパフォーマンスを継続的に計測する
kariad
6
16k
Xcodeのカバレッジ計測ではなぜブランチカバレッジが取れないのだろうか?
kariad
3
4.5k
Viewのテストどうしてますか?
kariad
2
1.5k
コードカバレッジとの付き合い方を知ってテストを書く
kariad
2
2.4k
開発者として学ぶソフトウェアテスト
kariad
2
1k
Other Decks in Programming
See All in Programming
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
1
420
AIエージェントはこう育てる - GitHub Copilot Agentとチームの共進化サイクル
koboriakira
0
390
PHP 8.4の新機能「プロパティフック」から学ぶオブジェクト指向設計とリスコフの置換原則
kentaroutakeda
2
570
20250628_非エンジニアがバイブコーディングしてみた
ponponmikankan
0
440
Cursor AI Agentと伴走する アプリケーションの高速リプレイス
daisuketakeda
1
130
たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server with Vanilla PHP
okashoi
1
190
VS Code Update for GitHub Copilot
74th
1
400
来たるべき 8.0 に備えて React 19 新機能と React Router 固有機能の取捨選択とすり合わせを考える
oukayuka
2
860
Kotlin エンジニアへ送る:Swift 案件に参加させられる日に備えて~似てるけど色々違う Swift の仕様 / from Kotlin to Swift
lovee
1
260
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
46
31k
Hypervel - A Coroutine Framework for Laravel Artisans
albertcht
1
100
なぜ「共通化」を考え、失敗を繰り返すのか
rinchoku
1
530
Featured
See All Featured
Build your cross-platform service in a week with App Engine
jlugia
231
18k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
44
2.4k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
107
19k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
It's Worth the Effort
3n
185
28k
Making the Leap to Tech Lead
cromwellryan
134
9.3k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Visualization
eitanlees
146
16k
How STYLIGHT went responsive
nonsquared
100
5.6k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
8
670
Rails Girls Zürich Keynote
gr2m
94
14k
Transcript
։ൃऀ͔ΒݟΔςετ 2019/06/15~16 WACATE2019 Ն BPPηογϣϯ kariad/͔Γ͋Ͳ(@kariad_uu) 1
ࣗݾհ • kariad / @kariad_uu • ยࢁ େथ • ΦΠγοΫεɾϥɾେ
iOS App Developer • / / V / ςετ / ઃܭ / ྉཧ / ήʔϜ 2
ςετͱͷؔΘΓ • ৽ଔ: SIer ͻͨ͢ΒؤுΔखಈςετɺऴΘΒͳ͍ɺؼΕͳ͍ɺਏ͍…. • ҟಈ: ΞδϟΠϧɺTDDɺςετָ͍͠ • స৬:
ΞδϟΠϧɺUnitςετಋೖɺςετ͍͖ͬͯ • QAΤϯδχΞɺςετΤϯδχΞͱݺΕΔํͱҰॹʹࣄΛͨ͜͠ ͱͳ͍Ͱ͢ 3
ࠓ͢͜ͱ • લճͷࢲͷϙδγϣϯϖʔύʔৼΓฦΓ • ։ൃऀ(ࢲ)͜͏͍͏෩ʹςετʹ͖߹ͬͯ·͢ͱ͍͏ - ςετͷϞνϕʔγϣϯ - ։ൃऀ͕ςετΛ͠ͳ͍(Ͱ͖ͳ͍)ཧ༝ 4
ҙʂ • ͋͘·Ͱࢲͷओ؍Ͱ͢ɻશͯͷ։ൃऀ͕͜͏Ͱ͋Δʂͱ͍ ͏Ͱͳ͍ͷͰྃ͝ঝ͍ͩ͘͞
લճ(WACATE2018 ౙ)ͷ ࢲͷϙδγϣϯϖʔύʔ 6
7
վΊͯݟͨ࣌ͷࢲͷײ • ΈΜͳͱൺͯϑΥϯτ͕Ͱ͔͍ • †ͱ͔ͬͯதೋප • ͳΜ͔ΤϞ͍͚ͩ • ਖ਼ͪΐͬͱஏ͔͔ͣͬͨ͠… 8
ͭ·ΓԿ͕ݴ͍͔ͨͬͨͷ͔ • ։ൃऀ͔ΒࢹͰͷςετͷ͍ ςετ͕͋Δ͜ͱͰ৺ཧత҆શੑͷ͋Δ։ൃ͕Ͱ͖Δ ͰΔ͔ΒʹޮతʹΓ͍ͨ • ςετ͕͖͔ͩΒྑͯ͘͠Ͷ ςετք۾ͷίϛϡχςΟॳࢀՃͩͬͨ 9
• ࠓճΓ߹͍ͱ͔΄΅͍ͳ͍ͷͰؾܰʹ͔͚ͯ͘͠ ΕΔͱخ͍͠Ͱ͢ 10
։ൃऀͷςετͷ͖߹͍ํ
ςετͷϞνϕʔγϣϯ (ࢲͳΓʹ) 12
• ࢲͨͪͷϛογϣϯϢʔβʔʹͱͬͯՁ͋Δ αʔϏεΛఏڙ͢Δ͜ͱ • Ձ͕͋Δ = ͓٬༷(Ϣʔβʔ)͕خ͍͔͠ 13
• Ͱ࠷ॳ͔Β࠷େԽ͞ΕͨՁΛఏڙ͢Δͷ͍͠ • ߟ͑ͨ͜ͱ͕ਖ਼͍͠(Ձ͕͋Δ)ͱݶΒͳ͍ ϦʔϯɾελʔτΞοϓ 14
ϦʔϯɾελʔτΞοϓ 15
ϦʔϯɾελʔτΞοϓ • ݁ہͲΜͳʹ͍͍ͷΛߟ͑ͨͱ͜ΖͰͦΕࢥ͍ࠐΈԾ આʹ͗͢ͳ͍ • MVP(Minimum Valuable Product)Ͱݕূ͢Δ • Ծઆ
→ Ծઆݕূ → ֶͼ →ҙࢥܾఆͷαΠΫϧΛճ͢ 16
࠷ۙͷτϨϯυʁ (σʔλੳΛݩʹͨ͠։ൃ) • ԾઆΛݩʹ࣮ͯ͠ϦϦʔε • σʔλΛऔಘͯ͠ੳ • ੳ݁ՌΛݩʹվળͯ͠ϦϦʔε
• ͲͪΒͱʹ͔͘ૣ͘αΠΫϧΛճ͍ͨ͠ • ͱ͍͑όά͕͋Γਖ਼ৗʹ͑ͳ͚ΕԾઆݕূͰ͖ͳ ͍ ࠷ݶCheckingߦ͍͍ͨ 18
• ΞδϟΠϧ։ൃऀͱͯ͠ૣ͘Ձͷ͋ΔαʔϏεΛఏ ڙ͢ΔͨΊʹCheckingΛ͍ͨ͠ ʢ࣭͕͔ͦͦͬͨΒՁԼ͕Δ…ʣ 19
͡Ό͋ͳΜͰChecking(ࠓճUnitςετ) Βͳ͍ͷʁ 20
• ΊΜͲ͍͘͞ • ςετʹڵຯ͕ͳ͍ • Γํ͕Θ͔Βͳ͍ • CheckingͷॏཁੑΛཧղ͍ͯ͠ͳ͍
• ΊΜͲ͍͘͞ • ςετʹڵຯ͕ͳ͍ • Γํ͕Θ͔Βͳ͍ • CheckingͷॏཁੑΛཧղ͍ͯ͠ͳ͍ ͬͱݴ͏ͱɺ Γͨͯ͘Ͱ͖ͳ͍
• ΊΜͲ͍͘͞ • ςετʹڵຯ͕ͳ͍ • Γํ͕Θ͔Βͳ͍ • CheckingͷॏཁੑΛཧղ͍ͯ͠ͳ͍ ͬͱݴ͏ͱɺ Γͨͯ͘Ͱ͖ͳ͍
ࢲ͕ߟ͑ΔUnitςετΛ Βͳ͍ཧ༝ͷେ͖ͳҰͭ
Unitςετ࣮ͬͯͦΜͳʹ ؆୯Ͱͳ͍ 24
ςετΛॻ͘ͷ͕͍͠ཧ༝ • ςετ͕ॻ͖͍͢ઃܭ(Testable)ʹͳ͍ͬͯͳ͍ͱॻ͘͜ ͱ͕͍͠ • (ແཧཧ)ॻ͚ͯځ۶Ͱมߋʹऑ͘ͳΔ • ޙ͔Βઃܭมߋຊʹେม…(ςετ͕ͳ͍ͱঘߋ) 25
• ଟ͘ͷ߹ઃܭʹ͕͋Δ • ͪΖΜϑϩʔతͳͷ߹͋Δ UnitςετΛॻ͘ϑϩʔʹͳ͍ͬͯͳ͍ɻ͕࣌ؒͳ͍ɻ 26
ͦͦઃܭͱ ”ؔ৺ͷʹΑͬͯ ෳࡶͳΛ୯७ͳͷ܈ͱͯ͠Γ͚Δ͜ͱ” -iOSΞϓϦઃܭύλʔϯೖ P23 ΑΓ 27
• ࠷ۙυϝΠϯతʹٕज़తʹෳࡶԽ͍ͯ͠Δ • ͦͷͨΊʹઃܭΛߦ͍ෳࡶ͞ʹཱ͔ͪ͏ • ͖ͪΜͱઃܭ͞Ε͍ͯΔ = ͕୯७Խ͞Ε͍ͯΔ TestableͷୈҰา
28
ઃܭύλʔϯ(ΞʔΩςΫνϟ) • MVC / MVP / MVVM / Clean Architecture
/ Flux… • ઃܭํͱͯ͠ͷϕετϓϥΫςΟεू • ͜ΕΒͷଟ͘Testableಉ࣌ʹߟ͑ΒΕ͍ͯΔ 29
ςετ͕ॻ͖͍͢ઃܭͱ • ͕୯७Խ͞Ε͍ͯΔ Ϟδϡʔϧ͝ͱʹςετ͍͕ͨ͠γϯϓϧͰ໌֬ • Ϟδϡʔϧؒͷґଘੑ͕ͳ͍ ςετ͍͕ͨ͠ଞϞδϡʔϧͷӨڹΛड͚ͳ͍ 30
ෳࡶͳΛ࣋ͬͨϞδϡʔϧ • xxઍߦΈ͍ͨͳΫϥε • ͗ͯ͢ԿΛ͍ͬͯΔͷ͔Θ͔Βͳ͍ϝιου • ͨΒͱίϝϯτͰઆ໌ͯ͋͠Δ(ݸਓతҙݟ) 31
աଟΛͲ͏ݟ͚Δ͔ʁ 32
“ίʔυͷΛௌ͘” 33
• ςετ͕ॻ͖ͮΒ͍… • ॲཧ͕ෳࡶԽ͖͍ͯͯ͠Δ… • ඞͣͦ͜ʹࠟ͋Δ • ίʔυͷष͍ͱ 34
ઃܭͷݪଇ • ༗໊ͳͷͰSOLIDݪଇ • ୯Ұݪଇɺ։์ดݪଇɺϦείϑͷஔݪଇɺΠϯ λʔϑΣʔεͷݪଇɺґଘؔٯసͷݪଇ • ઃܭͷݪଇΛ༻͍ͯίʔυͷष͍ΛऔΓআ͘
Ϟδϡʔϧؒͷґଘੑ͕͋Δ • ଞϞδϡʔϧͷ࣮͕มΘΔ͜ͱͰͪ͜Βͷॲཧ͕ӨڹΛड ͚ͯ͠·͏͜ͱ 36
func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) {
result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } 37
func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) {
result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } ͜͜ͷॲཧΛςετ͍ͨ͠ 38
func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) {
result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } SearchModelͷ࣮ʹ ґଘ͍ͯ͠Δ 39
func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) {
result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } SearchModelͷ࣮͕มΘΔͱ ͪ͜Βͷ݁ՌมΘͬͯ͠·͏ 40
func didTapSearchButton(text: String?, model: SearchModel) { ~~~ লུ ~~~ model.search(searchWord:
searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } ֎ଆ͔ΒΦϒδΣΫτΛ ͢Α͏ʹ 41
func didTapSearchButton(text: String?, model: SearchModelProtocol) { ~~~ লུ ~~~ model.search(searchWord:
searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } ͢ܕΛநԽ͞Εͨ ܕʹ͢Δ 42
43 protocol SearchEventModelProtocol { func search(searchWord: String, completion: @escaping ((Result<[ConnpassEvent]>)
-> ())) } Protocol
class FakeSearchEventModel: SearchEventModelProtocol { var search_successValue = [ConnpassEvent]() func search(searchWord:
String, completion: @escaping ((Result<[ConnpassEvent]>) -> ())) { search_callCount += 1 search_arguments = searchWord completion(.success(search_successValue)) } } class FakeFailureSearchEventModel: SearchEventModelProtocol { func search(searchWord: String, completion: @escaping ((Result<[ConnpassEvent]>) -> ())) { search_arguments = searchWord completion(.failure) } } ελϒ ґଘ͢ΔΦϒδΣΫτͷ݁ՌΛ ίϯτϩʔϧͰ͖Δ
• ͜ͷґଘ͢ΔΦϒδΣΫτΛ֎ଆ͔Β্ͯ͛͠Δ͜ͱ ΛDI(Dependency Injection)ͱ͍͏ • ͢ΦϒδΣΫτநԽ͞Ε͍ͯΔͷͰɺςετίʔ υ͔ΒελϒΛ͢͜ͱ͕Ͱ͖Δ • Modelͷ࣮ʹґଘͤͣޭɺࣦഊͦΕͧΕͷςετ͕ ՄೳͱͳΔ
45
• ࣮ࡍʹJavaͰ͋ΕMockitoΛ͑͏গָ͠ʹͳͬ ͨΓ͋Δ • ͕ɺSwiftͰϥΠϒϥϦΛ༻͍ͳ͍͜ͷύλʔϯ͕ൺֱ తଟ͍ • ͜ΕΒͷ͕ࣝͳ͍ͱॻ͘͜ͱ͕͍͠ 46
ઃܭҎ֎ͷཁૉ • ςεςΟϯάϑϨʔϜϫʔΫͷ͍ํ(XCTest, Quick/Nimble) • ඇಉظॲཧͷςετ • ಛʹඇಉظ׳Εͳ͍ͱ͍͠ iOSͰReactiveX(RxSwift)͕ελϯμʔυ(Combine) 47
ReactiveX • FRP(Functional Reactive Programming) • ͬ͘͟Γ͍͏ͱStreamͱ͍͏࣌ؒͷྲྀΕΛ༻͍ͯඇಉظॲཧΛ؆ ܿʹॻ͚ΔΑ͏ʹͨ͠ख๏ • บ͕͋ΔͷͰ׳Εͳ͍ͱগ͍͠͠
• ςετͰಉظతʹѻͬͯ͋͛ͨΓɺςετ༻ͷStreamͰཧͨ͠Γ 48
ςετΛॻͨ͘ΊʹΒͳ͖Ό ͍͚ͳ͍͜ͱଟ͍ 49
• Βͳ͍ͷͰͳ͘ΔεΩϧ͕ແ͔ͬͨ • Γͨͯ͘αΫοͱͰ͖ΔઃܭͰແ͔ͬͨ • Ͱ͜ͷลͷࣝɺͲ͜Ͱֶ͍͍ͷ…ʁ
ֶͿͨΊͷ໌Δ͍ஹ͠ 51
ίϛϡχςΟͷಇ͖ • ςετʹؔ͢Δษڧձ (TestNight) • ΫϥυϑΝϯσΟϯά(PEAKS)ʹΑΔ Android / iOSςετશॻץߦ(iOS8݄༧ఆ) 52
ٕज़ॻయ લճࢲ͜Μͳͷग़͠·ͨ͠
·ͱΊ • গͳ͘ͱϞόΠϧΞϓϦ։ൃऀͰςετʹର͢Δҙࣝ ؒҧ͍ͳ͘ߴ·͍ͬͯΔ(ͱࢥ͏) • αʔόɺϑϩϯτͳͲϑϨʔϜϫʔΫͷൃలΞδϟΠϧ ͷਁಁͱͱʹҙࣝߴ·͍ͬͯΔΑ͏ʹݟ͑Δ 54
• ؍ଌൣғͰςετΛͲ͏ʹ͔͍ͨ͠ͱࢥ͍ͬͯΔ։ൃ ऀ͕ଟ͍ • Ձ͋ΔαʔϏεΛಧ͚͍ͨͱ͍͏ࢥ͍୭͠ಉ͡ͳ ͣ • ։ൃऀ/ςετΤϯδχΞ͕ڠྗͯ͠ςετʹର͢Δݟ ΛਂΊ͍͚ͯͨΒخ͍͠ 55
• iOSΞϓϦઃܭύλʔϯೖ ؔ ོٛɾদؗ େًɾླ େوɾਿ্ ༸ฏɾ࢙ ᠳ৽ɾాத ݡ ࣏ɾՃ౻
ਓ ஶ https://peaks.cc/books/iOS_architecture • Clean Architecture ୡਓʹֶͿιϑτΣΞͷߏͱઃܭ Robert.C.Martinஶ ֯ యɾߴ ਖ਼߂ ༁ ࢀߟॻ੶
• Test Night https://testnight.connpass.com/ • Androidςετશॻ https://peaks.cc/books/android_testing • iOSςετશॻ https://peaks.cc/iOS_testing
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ 58