Slide 1

Slide 1 text

։ൃऀ͔ΒݟΔςετ 2019/06/15~16 WACATE2019 Ն BPPηογϣϯ kariad/͔Γ͋Ͳ(@kariad_uu) 1

Slide 2

Slide 2 text

ࣗݾ঺հ • kariad / @kariad_uu • ยࢁ େथ • ΦΠγοΫεɾϥɾେ஍ iOS App Developer • / / V / ςετ / ઃܭ / ྉཧ / ήʔϜ 2

Slide 3

Slide 3 text

ςετͱͷؔΘΓ • ৽ଔ: SIer ͻͨ͢ΒؤுΔखಈςετɺऴΘΒͳ͍ɺؼΕͳ͍ɺਏ͍…. • ҟಈ: ΞδϟΠϧɺTDDɺςετָ͍͠ • స৬: ΞδϟΠϧɺUnitςετಋೖɺςετ΍͍͖ͬͯ • QAΤϯδχΞɺςετΤϯδχΞͱݺ͹ΕΔํͱҰॹʹ࢓ࣄΛͨ͜͠ ͱ͸ͳ͍Ͱ͢ 3

Slide 4

Slide 4 text

ࠓ೔࿩͢͜ͱ • લճͷࢲͷϙδγϣϯϖʔύʔৼΓฦΓ • ։ൃऀ(ࢲ)͸͜͏͍͏෩ʹςετʹ޲͖߹ͬͯ·͢ͱ͍͏࿩
 - ςετ΁ͷϞνϕʔγϣϯ
 - ։ൃऀ͕ςετΛ͠ͳ͍(Ͱ͖ͳ͍)ཧ༝ 4

Slide 5

Slide 5 text

஫ҙʂ • ͋͘·Ͱ΋ࢲͷओ؍Ͱ͢ɻશͯͷ։ൃऀ͕͜͏Ͱ͋Δʂͱ͍ ͏࿩Ͱ͸ͳ͍ͷͰྃ͝ঝ͍ͩ͘͞

Slide 6

Slide 6 text

લճ(WACATE2018 ౙ)ͷ ࢲͷϙδγϣϯϖʔύʔ 6

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

౰೔վΊͯݟͨ࣌ͷࢲͷײ૝ • ΈΜͳͱൺ΂ͯϑΥϯτ͕Ͱ͔͍ • †ͱ͔࢖ͬͯதೋප • ͳΜ͔ΤϞ͍͚ͩ • ਖ਼௚ͪΐͬͱஏ͔͔ͣͬͨ͠… 8

Slide 9

Slide 9 text

ͭ·ΓԿ͕ݴ͍͔ͨͬͨͷ͔ • ։ൃऀ͔Βࢹ఺Ͱͷςετ΁ͷ૝͍
 ςετ͕͋Δ͜ͱͰ৺ཧత҆શੑͷ͋Δ։ൃ͕Ͱ͖Δ
 Ͱ΋΍Δ͔Βʹ͸ޮ཰తʹ΍Γ͍ͨ • ςετ͕޷͖͔ͩΒ஥ྑͯ͘͠Ͷ
 ςετք۾ͷίϛϡχςΟॳࢀՃͩͬͨ 9

Slide 10

Slide 10 text

• ࠓճ΋஌Γ߹͍ͱ͔΄΅͍ͳ͍ͷͰؾܰʹ࿩͔͚ͯ͘͠ ΕΔͱخ͍͠Ͱ͢ 10

Slide 11

Slide 11 text

։ൃऀͷςετ΁ͷ޲͖߹͍ํ

Slide 12

Slide 12 text

ςετ΁ͷϞνϕʔγϣϯ (ࢲͳΓʹ) 12

Slide 13

Slide 13 text

• ࢲͨͪͷϛογϣϯ͸ϢʔβʔʹͱͬͯՁ஋͋Δ
 αʔϏεΛఏڙ͢Δ͜ͱ • Ձ஋͕͋Δ = ͓٬༷(Ϣʔβʔ)͕خ͍͔͠ 13

Slide 14

Slide 14 text

• Ͱ΋࠷ॳ͔Β࠷େԽ͞ΕͨՁ஋Λఏڙ͢Δͷ͸೉͍͠ • ߟ͑ͨ͜ͱ͕ਖ਼͍͠(Ձ஋͕͋Δ)ͱ͸ݶΒͳ͍
 ϦʔϯɾελʔτΞοϓ 14

Slide 15

Slide 15 text

ϦʔϯɾελʔτΞοϓ 15

Slide 16

Slide 16 text

ϦʔϯɾελʔτΞοϓ • ݁ہͲΜͳʹ͍͍΋ͷΛߟ͑ͨͱ͜ΖͰͦΕ͸ࢥ͍ࠐΈ΍Ծ આʹ͗͢ͳ͍ • MVP(Minimum Valuable Product)Ͱݕূ͢Δ • Ծઆ → Ծઆݕূ → ֶͼ →ҙࢥܾఆͷαΠΫϧΛճ͢ 16

Slide 17

Slide 17 text

࠷ۙͷτϨϯυʁ
 (σʔλ෼ੳΛݩʹͨ͠։ൃ) • ԾઆΛݩʹ࣮૷ͯ͠ϦϦʔε • σʔλΛऔಘͯ͠෼ੳ • ෼ੳ݁ՌΛݩʹվળͯ͠ϦϦʔε

Slide 18

Slide 18 text

• ͲͪΒ΋ͱʹ͔͘ૣ͘αΠΫϧΛճ͍ͨ͠ • ͱ͸͍͑όά͕͋Γਖ਼ৗʹ࢖͑ͳ͚Ε͹ԾઆݕূͰ͖ͳ ͍
 ‎ ࠷௿ݶChecking͸ߦ͍͍ͨ 18

Slide 19

Slide 19 text

• ΞδϟΠϧ։ൃऀͱͯ͠͸ૣ͘Ձ஋ͷ͋ΔαʔϏεΛఏ ڙ͢ΔͨΊʹCheckingΛ͍ͨ͠
 ʢͦ΋ͦ΋඼࣭͕௿͔ͬͨΒՁ஋΋Լ͕Δ…ʣ 19

Slide 20

Slide 20 text

͡Ό͋ͳΜͰChecking(ࠓճ͸Unitςετ) ΍Βͳ͍ͷʁ 20

Slide 21

Slide 21 text

• ΊΜͲ͍͘͞ • ςετʹڵຯ͕ͳ͍ • ΍Γํ͕Θ͔Βͳ͍ • CheckingͷॏཁੑΛཧղ͍ͯ͠ͳ͍

Slide 22

Slide 22 text

• ΊΜͲ͍͘͞ • ςετʹڵຯ͕ͳ͍ • ΍Γํ͕Θ͔Βͳ͍ • CheckingͷॏཁੑΛཧղ͍ͯ͠ͳ͍ ΋ͬͱݴ͏ͱɺ ΍Γͨͯ͘΋Ͱ͖ͳ͍

Slide 23

Slide 23 text

• ΊΜͲ͍͘͞ • ςετʹڵຯ͕ͳ͍ • ΍Γํ͕Θ͔Βͳ͍ • CheckingͷॏཁੑΛཧղ͍ͯ͠ͳ͍ ΋ͬͱݴ͏ͱɺ ΍Γͨͯ͘΋Ͱ͖ͳ͍ ࢲ͕ߟ͑ΔUnitςετΛ ΍Βͳ͍ཧ༝ͷେ͖ͳҰͭ

Slide 24

Slide 24 text

Unitςετ࣮ͬͯ͸ͦΜͳʹ ؆୯Ͱ͸ͳ͍ 24

Slide 25

Slide 25 text

ςετΛॻ͘ͷ͕೉͍͠ཧ༝ • ςετ͕ॻ͖΍͍͢ઃܭ(Testable)ʹͳ͍ͬͯͳ͍ͱॻ͘͜ ͱ͕೉͍͠ • (ແཧ໼ཧ)ॻ͚ͯ΋ځ۶Ͱมߋʹ΋ऑ͘ͳΔ • ޙ͔Βઃܭมߋ͸ຊ౰ʹେม…(ςετ͕ͳ͍ͱঘߋ) 25

Slide 26

Slide 26 text

• ଟ͘ͷ৔߹͸ઃܭʹ໰୊͕͋Δ • ΋ͪΖΜϑϩʔతͳ໰୊ͷ৔߹΋͋Δ
 UnitςετΛॻ͘ϑϩʔʹͳ͍ͬͯͳ͍ɻ͕࣌ؒͳ͍ɻ 26

Slide 27

Slide 27 text

ͦ΋ͦ΋ઃܭͱ͸ ”ؔ৺ͷ෼཭ʹΑͬͯ
 ෳࡶͳ໰୊Λ୯७ͳ໰୊ͷ܈ͱͯ͠੾Γ෼͚Δ͜ͱ” -iOSΞϓϦઃܭύλʔϯೖ໳ P23 ΑΓ 27

Slide 28

Slide 28 text

• ࠷ۙ͸υϝΠϯతʹ΋ٕज़తʹ΋ෳࡶԽ͍ͯ͠Δ • ͦͷͨΊʹઃܭΛߦ͍ෳࡶ͞ʹཱͪ޲͔͏ • ͖ͪΜͱઃܭ͞Ε͍ͯΔ = ໰୊͕୯७Խ͞Ε͍ͯΔ
 ‎ Testable΁ͷୈҰา 28

Slide 29

Slide 29 text

ઃܭύλʔϯ(ΞʔΩςΫνϟ) • MVC / MVP / MVVM / Clean Architecture / Flux… • ઃܭํ਑ͱͯ͠ͷϕετϓϥΫςΟεू • ͜ΕΒͷଟ͘͸Testable΋ಉ࣌ʹߟ͑ΒΕ͍ͯΔ 29

Slide 30

Slide 30 text

ςετ͕ॻ͖΍͍͢ઃܭͱ͸ • ໰୊͕୯७Խ͞Ε͍ͯΔ
 Ϟδϡʔϧ͝ͱʹςετ͍ͨ͠੹຿͕γϯϓϧͰ໌֬ • Ϟδϡʔϧؒͷґଘੑ͕ͳ͍
 ςετ͍ͨ͠੹຿͕ଞϞδϡʔϧͷӨڹΛड͚ͳ͍ 30

Slide 31

Slide 31 text

ෳࡶͳ੹຿Λ࣋ͬͨϞδϡʔϧ • xxઍߦΈ͍ͨͳΫϥε • ௕͗ͯ͢΋͸΍ԿΛ΍͍ͬͯΔͷ͔Θ͔Βͳ͍ϝιου • ΍ͨΒͱίϝϯτͰઆ໌ͯ͋͠Δ(ݸਓతҙݟ) 31

Slide 32

Slide 32 text

੹຿աଟΛͲ͏ݟ෼͚Δ͔ʁ 32

Slide 33

Slide 33 text

“ίʔυͷ੠Λௌ͘” 33

Slide 34

Slide 34 text

• ςετ͕ॻ͖ͮΒ͍… • ॲཧ͕ෳࡶԽ͖͍ͯͯ͠Δ… • ඞͣͦ͜ʹࠟ੻͸͋Δ • ίʔυͷष͍ͱ΋ 34

Slide 35

Slide 35 text

ઃܭͷݪଇ • ༗໊ͳ΋ͷͰ͸SOLIDݪଇ • ୯Ұ੹೚ݪଇɺ։์ด࠯ݪଇɺϦείϑͷஔ׵ݪଇɺΠϯ λʔϑΣʔε෼཭ͷݪଇɺґଘؔ܎ٯసͷݪଇ • ઃܭͷݪଇΛ༻͍ͯίʔυͷष͍ΛऔΓআ͘

Slide 36

Slide 36 text

Ϟδϡʔϧؒͷґଘੑ͕͋Δ • ଞϞδϡʔϧͷ࣮૷͕มΘΔ͜ͱͰͪ͜Βͷॲཧ͕ӨڹΛड ͚ͯ͠·͏͜ͱ 36

Slide 37

Slide 37 text

func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } 37

Slide 38

Slide 38 text

func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } ͜͜ͷॲཧΛςετ͍ͨ͠ 38

Slide 39

Slide 39 text

func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } SearchModelͷ࣮૷ʹ ґଘ͍ͯ͠Δ 39

Slide 40

Slide 40 text

func didTapSearchButton(text: String?) { ~~~ লུ ~~~ SearchModel().search(searchWord: searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } SearchModelͷ࣮૷͕มΘΔͱ ͪ͜Βͷ݁Ռ΋มΘͬͯ͠·͏ 40

Slide 41

Slide 41 text

func didTapSearchButton(text: String?, model: SearchModel) { ~~~ লུ ~~~ model.search(searchWord: searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } ֎ଆ͔ΒΦϒδΣΫτΛ ౉͢Α͏ʹ 41

Slide 42

Slide 42 text

func didTapSearchButton(text: String?, model: SearchModelProtocol) { ~~~ লུ ~~~ model.search(searchWord: searchWord) { result in switch result { case .success(let events): ~~ লུ ~~ case .failure: ~~ লུ ~~ } } } ౉͢ܕΛந৅Խ͞Εͨ ܕʹ͢Δ 42

Slide 43

Slide 43 text

43 protocol SearchEventModelProtocol { func search(searchWord: String, completion: @escaping ((Result<[ConnpassEvent]>) -> ())) } Protocol

Slide 44

Slide 44 text

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) } } ελϒ ґଘ͢ΔΦϒδΣΫτͷ݁ՌΛ ίϯτϩʔϧͰ͖Δ

Slide 45

Slide 45 text

• ͜ͷґଘ͢ΔΦϒδΣΫτΛ֎ଆ͔Β౉্ͯ͛͠Δ͜ͱ ΛDI(Dependency Injection)ͱ͍͏ • ౉͢ΦϒδΣΫτ͸ந৅Խ͞Ε͍ͯΔͷͰɺςετίʔ υ͔Β͸ελϒΛ౉͢͜ͱ͕Ͱ͖Δ • Modelͷ࣮૷ʹґଘͤͣ੒ޭɺࣦഊͦΕͧΕͷςετ͕ ՄೳͱͳΔ 45

Slide 46

Slide 46 text

• ࣮ࡍʹ͸JavaͰ͋Ε͹MockitoΛ࢖͑͹΋͏গָ͠ʹͳͬ ͨΓ͸͋Δ • ͕ɺSwiftͰ͸ϥΠϒϥϦΛ༻͍ͳ͍͜ͷύλʔϯ͕ൺֱ తଟ͍ • ͜ΕΒͷ஌͕ࣝͳ͍ͱॻ͘͜ͱ͕೉͍͠ 46

Slide 47

Slide 47 text

ઃܭҎ֎ͷཁૉ • ςεςΟϯάϑϨʔϜϫʔΫͷ࢖͍ํ(XCTest, Quick/Nimble) • ඇಉظॲཧͷςετ • ಛʹඇಉظ͸׳Εͳ͍ͱ೉͍͠
 iOSͰ͸ReactiveX(RxSwift)͕ελϯμʔυ(Combine) 47

Slide 48

Slide 48 text

ReactiveX • FRP(Functional Reactive Programming) • ௒ͬ͘͟Γ͍͏ͱStreamͱ͍͏࣌ؒͷྲྀΕΛ༻͍ͯඇಉظॲཧΛ؆ ܿʹॻ͚ΔΑ͏ʹͨ͠ख๏ • บ͕͋ΔͷͰ׳Εͳ͍ͱগ͠೉͍͠ • ςετͰ΋ಉظతʹѻͬͯ͋͛ͨΓɺςετ༻ͷStreamͰ؅ཧͨ͠Γ 48

Slide 49

Slide 49 text

ςετΛॻͨ͘Ίʹ஌Βͳ͖Ό ͍͚ͳ͍͜ͱ͸ଟ͍ 49

Slide 50

Slide 50 text

• ΍Βͳ͍ͷͰ͸ͳ͘΍ΔεΩϧ͕ແ͔ͬͨ • ΍Γͨͯ͘΋αΫοͱͰ͖ΔઃܭͰ͸ແ͔ͬͨ • Ͱ΋͜ͷลͷ஌ࣝɺͲ͜Ͱֶ΂͹͍͍ͷ…ʁ

Slide 51

Slide 51 text

ֶͿͨΊͷ໌Δ͍ஹ͠ 51

Slide 52

Slide 52 text

ίϛϡχςΟͷಇ͖ • ςετʹؔ͢Δษڧձ (TestNight) • Ϋϥ΢υϑΝ΢ϯσΟϯά(PEAKS)ʹΑΔ
 Android / iOSςετશॻץߦ(iOS͸8݄༧ఆ) 52

Slide 53

Slide 53 text

ٕज़ॻయ લճࢲ΋͜Μͳͷग़͠·ͨ͠

Slide 54

Slide 54 text

·ͱΊ • গͳ͘ͱ΋ϞόΠϧΞϓϦ։ൃऀ಺Ͱ΋ςετʹର͢Δҙࣝ ͸ؒҧ͍ͳ͘ߴ·͍ͬͯΔ(ͱࢥ͏) • αʔόɺϑϩϯτͳͲ΋ϑϨʔϜϫʔΫͷൃల΍ΞδϟΠϧ ͷਁಁͱͱ΋ʹҙࣝ͸ߴ·͍ͬͯΔΑ͏ʹݟ͑Δ 54

Slide 55

Slide 55 text

• ؍ଌൣғͰ͸ςετΛͲ͏ʹ͔͍ͨ͠ͱࢥ͍ͬͯΔ։ൃ ऀ͕ଟ͍ • Ձ஋͋ΔαʔϏεΛಧ͚͍ͨͱ͍͏ࢥ͍͸୭͠΋ಉ͡ͳ ͸ͣ • ։ൃऀ/ςετΤϯδχΞ͕ڠྗͯ͠ςετʹର͢Δ஌ݟ ΛਂΊ͍͚ͯͨΒخ͍͠ 55

Slide 56

Slide 56 text

• iOSΞϓϦઃܭύλʔϯೖ໳
 ؔ ོٛɾদؗ େًɾླ໦ େوɾਿ্ ༸ฏɾ࢙ ᠳ৽ɾాத ݡ ࣏ɾՃ౻ ׮ਓ ஶ
 https://peaks.cc/books/iOS_architecture • Clean Architecture ୡਓʹֶͿιϑτ΢ΣΞͷߏ଄ͱઃܭ
 Robert.C.Martinஶ ֯ ੐యɾߴ໦ ਖ਼߂ ༁ ࢀߟॻ੶

Slide 57

Slide 57 text

• Test Night
 https://testnight.connpass.com/ • Androidςετશॻ
 https://peaks.cc/books/android_testing • iOSςετશॻ
 https://peaks.cc/iOS_testing

Slide 58

Slide 58 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ 58