Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
不安定なテストは200種類あんねん
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
yimajo
October 27, 2023
Programming
1.3k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
不安定なテストは200種類あんねん
#ios_test_night
https://testnight.connpass.com/event/295913/
yimajo
October 27, 2023
More Decks by yimajo
See All by yimajo
片田舎のおっさん、 Swift Buildのダイアモンド問題解決の不具合修正PRを出すが、解決方法がキャッシュをしないようにすることであり、ビルド時間が伸びると言われてマージされないので高速化もする/swiftbuild
yimajo
0
420
OSSとなったswift-buildで Xcodeのビルドを差し替えられるため 自分でXcodeを直せる時代になっている ダイアモンド問題編
yimajo
3
960
いま中途半端なSwift 6対応をするより、Default ActorやApproachable Concurrencyを有効にしてからでいいんじゃない?
yimajo
2
780
11年かかって やっとVibe Codingに 時代が追いつきましたね
yimajo
1
420
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
3
360
良いテストコードのために悪いテストコードを理解する - 不安定なテスト編: iOSアプリ開発ユニットテストの場合
yimajo
22
6.9k
TCAの Shared Stateって どういう仕組みになってんの?
yimajo
0
2.8k
Swift 5.9 からの Observation はiOS17 未満 からも使えて struct の変更検知もできるんすかね?
yimajo
2
1.2k
TCA v0.19.0からのSwitchStore/CaseLetが良い
yimajo
0
2.1k
Other Decks in Programming
See All in Programming
Performance Optimization With LLMs
xleitix
0
240
Spec-Driven Development - Why Vaadin Is the Perfect Fit
simas
PRO
0
220
Getting Started with Vibe Coding
miccheng
0
180
Spec-driven Development with AI
simas
PRO
0
2.2k
Goコンパイラを作ってGoをもっと理解する / Build a Go compiler to better understand Go
ikura_hamu
0
120
PyConTW 2026 - AI Coding 不只是工具升級, 區域醫院醫療資訊系統協作開發與資訊團隊轉型實踐
cclai999
0
230
TanStack Vue Query と Pinia Colada から学ぶ非同期状態の型設計
aoshi
0
610
あなたの await、OS まで届いてる? strace で覗く JavaScript の非同期
hasutosasaki
1
2k
エージェントを使い続けるためのメタワーク / Meta-work for keeping using the agent
rkaga
6
2.2k
Enforcing Your Architecture with Unit Tests in the AI Era (Swift Connection X)
steliosf
PRO
1
130
自作Discord音声botをLambda MicroVMsに移行した話 / Migrating My Discord Voice Bot to Lambda MicroVMs
akihisaikeda
1
280
Kotlinize Is Not Just Converting to Kotlin
chigichan24
1
220
Featured
See All Featured
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.5k
Balancing Empowerment & Direction
lara
6
1.4k
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
550
Odyssey Design
rkendrick25
PRO
2
920
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.6k
How to Talk to Developers About Accessibility
jct
2
610
The Limits of Empathy - UXLibs8
cassininazir
2
760
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3.1k
Ten Tips & Tricks for a 🌱 transition
stuffmc
1
280
Transcript
ෆ҆ఆͳςετछྨ͋ΜͶΜ #ios_test_night 2023.10 y.imajo
ࣗݾհ
ࣗݾհ • ৬ྺ • ৽ଔ: ಋମϝʔΧʔͰςετΤϯδχΞ • WebϞόΠϧΞϓϦ։ൃͷιϑτΣΞΤϯδχΞ • Now!:
ϓϩαφʔ݉ιϑτΣΞΤϯδχΞ
ຊ ෆ҆ఆͳςετͷݪҼΛཧ ͠ղܾ͢ΔҊ
ࠓճࢲ͕͢ʮෆ҆ఆͳςετʯͷఆٛ • ςετίʔυʹΑΓςετࣗಈԽ͞Εɺͦͷ݁Ռ͕ޭͨ͠Γࣦഊ͠ ͨΓͱෆ҆ఆͳ୯ମςετͷ͜ͱ • ඞͣޭ͢Δ୯ମςετෆ҆ఆͰͳ͍ • ඞࣦͣഊ͢Δ୯ମςετෆ҆ఆͰͳ͍
ࢲͷʮෆ҆ఆͳςετʯͷఆٛ ޭ ࣦഊ ෆ҆ఆͳςετ ʢݪҼ͍͍ͩͨݟΔਓʹΑͬͯʣ200छྨ͋ΜͶΜ
ݪҼͷྨ • େྨ1: ςετίʔυ·ͨςετରͷϓϩμΫγϣϯίʔυ͕ѱ͍ • ݪҼ1: ࣮ߦ࣌ʹܾఆ͢ΔಈతͳʹΑΔςετ࣮ߦ • ݪҼ2: OSSϑϨʔϜϫʔΫཧղෆ
• ݪҼ3: ςετέʔεͷ࣮ߦॱংґଘ • ݪҼ4: ϓϩάϥϛϯάݴޠ/SDKͷཧղෆ • ݪҼ ͦͷଞ • େྨ2: ςετ࣮ߦϚγϯཁҼʢڥʣ ࠓ͢͜ͱ
ݪҼ1: ࣮ߦ࣌ʹܾఆ͢ΔಈతͳʹΑΔςετ࣮ߦ • ྫ • Foundation.Date.init() / Foundation.Date.now • ݺͼग़͞Εͨࡍͷ࣌ؒΛऔಘ͢Δίʔυ
ྫ: Foundation.Date.init() / Foundation.Date.now • ͳͥෆ҆ఆ͞ʹͭͳ͕Δͷ͔ • ݻఆͰͳ͍ͨΊ • ݱࡏ࣌ࠁͷ༻ҙ͔Βར༻͢Δ·Ͱఆ֎ʹ͕࣌ؒܦա͢Δ
• ͲͷΑ͏ͳ߹ʹࠔΔͷ͔ • ςετରͷϝιουͰNඵҎͳͲͷॲཧ͕͋Δ • ςετίʔυࣗମͰͷൺֱʹ͏ͷ͔͍ͬ • Ͳ͏ղܾ͢Δͷ͔ • Date.init()/Date.nowʹΑΔݱࡏ࣌ࠁऔಘ෭࡞༻࣮ߦͱߟ͑ɺDIͨ͠ͷΛར༻͢Δ • Ͱ͖Δ͚ͩݻఆʢfixtureʣΛ͏ ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹܾఆ͢ΔಈతͳʹΑΔςετ࣮ߦ
ิ: ࣮ߦ࣌ʹಈతʹ͕ܾఆ͢ΔͭΒ • Foundation.Date / Calendar / Locale / UUID
• Swift Standard LibraryͷContinuousClock • ͳͲͷཚੜ ෆ҆ఆͳݪҼ: ࣮ߦ࣌ʹܾఆ͢ΔಈతͳʹΑΔςετ࣮ߦ https://github.com/pointfreeco/swift-dependencies/tree/main/ Sources/Dependencies/DependencyValues ࢀߟ: pointfreeco/swift-dependencies
ݪҼ2: OSSϑϨʔϜϫʔΫཧղෆ • ྫ • QuickͰDate()ΛbeforeEachΘͣݺͼग़ͯ͠͠·͏ • RxSwiftͰεέδϡʔϥΛDIͤͣʹςετͯ͠͠·͏ • OHHTTPStubs
/ AutoMockable • ࠓճআ֎
ྫ: QuickͰDate()ΛbeforeEachΘͣݺͼग़ͯ͠͠·͏ • ͳͥෆ҆ఆ͞ʹͭͳ͕Δͷ͔ • Quickςετ࣮ߦͷͨΊʹ·ͣ༻ҙͯ͋͠ΔQuickSpecશ͕ͯॳ ظԽ͞ΕɺςετରΛूΊͨ͋ͱʹςετΛॱ࣮࣍ߦ͢Δ • ݱࡏ࣌ࠁͷ༻ҙ͔Βར༻͢Δ·Ͱఆ֎ʹ͕࣌ؒܦա͢Δ •
Ͳ͏ղܾ͢Δͷ͔ • beforeEach͏ ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ
class SpecA: QuickSpec { override class func spec() { describe("A")
{ print("A.describe") let date = Date() // beforeEachΛΘͳ͍ྫ it("actionϝιου1ඵҎʹ࣮ߦ͕ྃ͢Δ") { print("A.it") let subject = ... subject.action() expect(date - Date()) < 1.0 // ྫ } } } } class SpecB: QuickSpec { override class func spec() { describe(“B") { print("B.describe") it(“…”) { print("B.it") } } } } A.describe B.describe A.it B.it it࠷ޙʹ࣮ߦ͞ΕΔ • SpecAͷspecϝιου͕࣮ߦ͞ΕΔ • SpecAͷdescribe͕࣮ߦ͞ΕΔ • SpecBͷspecϝιου͕࣮ߦ͞ΕΔ • SpecBͷdescribe͕࣮ߦ͞ΕΔ • SpecAͷit͕࣮ߦ͞ΕΔ • SpecBͷit͕࣮ߦ͞ΕΔ print ݁Ռ
ͳΜͰ͜Μͳ͜ͱʹ…?
XcodeͰͷςετ࣮ߦ (xcodebuild test ܥ) ϑΣʔζͱͯ͠ ςετͷ ొ ͱ ࣮ߦ
ͷ2ϑΣʔζʹ ͔Ε͍ͯͯ Quick ͦΕΛར༻͍ͯ͠Δɻ ςετొϑΣʔζ ςετ࣮ߦϑΣʔζ XCTestCaseͷdefaultTestSuite XCTestCaseͷtestInvocations ิBQQMFTXJGUUFTUJOHͰݱঢ়ςετొͯ͠ΔΓํͱҧ͏
XCTest Quick ϑΣʔζ1. XcodeXCTestCaseΛܧঝͨ͠Πϯελϯε͔ΒdefaultTestSuiteΛ࣮ߦ͢Δ ϑΣʔζ1_1. defaultTestSuite͔ΒgatherExamplesNeeded()͕ݺͼग़͞ΕΔ ϑΣʔζ1_2. gatherExamplesNeeded()spec()Λ࣮ߦ ϑΣʔζ1_3. spec()ʹ͋ΔbeforeEach()/it()ΛWorldʹappend͍ͯ͘͠
ϑΣʔζ2. XcodeXCTestCaseܧঝ͍ͯ͠ΔΠϯελϯε͔ΒtestInvocationsΛ࣮ߦ͢Δ ϑΣʔζ2_1.testInvocationsWorldʹappend͞ΕͯΔͷ͔ΒऔΓग़͠itΛςετͱ࣮ͯ͠ߦ beforeEach/it४උϑΣʔζͰอ࣋͞Ε ࣮ߦϑΣʔζͰ࣮ߦ͞ΕΔ
͓·͚: ৗʹࣦഊ͢Δྫ Presenter͕ղ์͞Ε݁Ռࣦഊɻ itΫϩʔδϟ͕࣮ߦ͞ΕΔ࣌ ϑΣʔζ2Ͱ͋ΓɺϑΣʔζ1Ͱ࡞ ͞ΕͨPresenterࢀর͞Εͳ͍ͷ Ͱʢେʣղ์͞ΕΔ͜ͱͰdeinit ͕࣮ߦ͞Ε݁Ռ͕มΘΔɻ class SpecC:
QuickSpec { override class func spec() { describe(“Presenter.viewDidLoad࣮ߦ”) { // ·ͨbeforeEach͠ͳ͍ let spy = View() let presenter = Presenter() // SUT presenter.view = spy // Action४උϑΣʔζͰͬͯ͠·͏ presenter.viewDidLoad() ɹɹ it("Viewͷ͕ViewDidLoadͰtrueʹͳͬͯΔ") { expect(spy.flag).to(beTrue()) // ݁Ռʁ } } } } class Presenter { var view: View? deinit { view?.flag = false } func viewDidLoad() { view?.flag = true } }
ྫ: RxSwiftͰεέδϡʔϥΛDIͤͣςετͯ͠͠·͏ • ͳͥෆ҆ఆ͞ʹͭͳ͕Δͷ͔ • MainSchedulerಉظతʹΛྲྀ͢ͱ͖͋Δ͠ඇಉظతʹΛྲྀ ͢͜ͱ͋Δ • Ͳ͏ղܾ͢Δͷ͔ •
εέδϡʔϥΛDI͠ςετεέδϡʔϥΛ͏ • RxSwiftͷίʔυΛಡΉ͔RxSwiftݚڀಡຊΛങ͏ ෆ҆ఆͳݪҼ:OSSϑϨʔϜϫʔΫཧղෆ
ݪҼ3: ςετέʔεͷ࣮ߦॱংґଘ • ྫ • ςετରΛෳςετέʔεͰ͍ճ͠ϓϩύςΟΛηοτ
ྫ: ςετରΛෳςετέʔεͰ͍ճ͠ϓϩύςΟΛηοτ • ͳͥෆ҆ఆ͞ʹͭͳ͕Δͷ͔ • ςετରTest Doubleʹ͏ΦϒδΣΫτΛෳςετͰ͍ ճͯ͠͠·͏ͱͦͷϓϩύςΟΛηοτ͠ΕͨΓϦηοτ͠Ε Δ •
Ͳ͏ղܾ͢Δͷ͔ • ෳςετέʔεͰ͍·Θ͞ͳ͍ ෆ҆ఆͳݪҼ:ςετέʔεͷ࣮ߦॱংґଘ
class SpecD: QuickSpec { override class func spec() { describe(“ະఆ͢Δ")
{ var subject = … var user = TestDouble.User() context(“ॴ͕ຊͰ10ࡀ”) { beforeEach { user.age = 10 subject.user = user } it("ະఆ͞ΕΔ") { subject.action() expect(subject.ະ).to(beTruth)) } } context(“ॴҟੈքͰ10ࡀ”) { beforeEach { user.age = 10 subject.user = user subject.location = .ҟੈք } it(“ະఆ͞Εͳ͍") { subject.action() expect(subject.ະ).to(beFalse)) } } ྫ: ্ͷຊcontextͰlocationΛ ઃఆ͍ͯ͠ͳ͍͕σϑΥϧτͷ locationͰઌʹςετޭ͠ɺ࣍ ʹԼͷҟੈքcontextͰ໌ࣔͯ͠ ςετޭ͍ͯ͠Δͱ͢Δɻ ઌʹԼͷҟੈքcontext͕࣮ߦ͞Ε Δͱ…?
Quick ͕ѱ͍Θ͚͡Όͳ͘ɺςετରӨڹ Λٴ΅͢ΦϒδΣΫτ͕ςετ֎͔ΒӨڹΛड ͚Δ͜ͱΛՄೳʹͯ͠͠·͍ͬͯΕɺςετ ݁Ռҙਤͤͣෆ҆ఆʹͳΓ͍͢ɻ
ͨͩͪΐͬͱ͜Εʹؔͯ͠QuickΛͬͯͳ ͍ͱ͍͚ͳ͍͜ͱ͕ଟ͍͔ͳͱࢥ͏
ઌड़ͷ௨Γɺ QuickͰbeforeEach४උϑΣʔζͰͳ࣮͘ߦϑΣʔζͰ͋ ΓɺมͷॳظԽΒ࠶ઃఆʢArrangeʣͰ࣮֬ʹΘ͍ͤͨΜ ͚ͩͲɺbeforeEachΫϩʔδϟͳͷͰArrangeͷνΣοΫΛ͢ ΔΑ͏ͳػೳͳ͍ɻ
ܕͷػೳΛར༻ͯ͠ܕ҆৺ͳςετͷArrange͕Ͱ͖Εͬͱ ྑ͍ͱࢥ͑Δ…ɻ XCTestCaseࣗମͰؤுΕͰ͖Δɻ apple/swift-testingͬͨΒ͞Βʹදݱྗߴ͘Ͱ͖ͦ͏…ɻ
ݪҼ4: ϓϩάϥϛϯάݴޠ/SDKͷཧղෆ • ྫ • ϝΠϯλʔήοτͰͷςετϗετΞϓϦέʔγϣϯ͕ىಈͯ͠ ͠·͏ • XCTestCase.waitϝιουʹΑΓεϨου͕σουϩοΫ͢Δͱ͖ ͕͋Δ
ྫ: ϝΠϯλʔήοτͰͷςετϗετΞϓϦέʔγϣϯ͕ىಈͯ͠͠·͏ • ͳͥෆ҆ఆ͞ʹͭͳ͕Δͷ͔ • ઃఆͱͯ͠ɺςετϞδϡʔϧΛϗετΞϓϦຊମͱࢦఆ͢Δͱςετ࣌ʹΞϓϦ͕ ىಈ͠ɺͦ͜Ͱ෭࡞༻Λ࣮ߦ͠ςετʹӨڹΛ༩͑Δ͜ͱ͕͋Δ • Ͳ͏ղܾ͢Δͷ͔ •
AppDelegateΛMockͯ͠Կ͠ͳ͍ʢor Mockͤͣݕ͠ذͯ͠Կ͠ͳ͍ʣ • ϗετΞϓϦͷϞδϡʔϧSwiftUI.AppAppDelegateͷΈ࣋ͪɺͦΕҎ֎ͷॲཧ Ϟδϡʔϧͱׂ͠ɺͦΕʹରͯ͠ςετλʔήοτϞδϡʔϧΛ࡞Δ • XcodeઃఆͰHost Application = Noneʹͯ͠ςετ࣮ߦͰ͖Ε͍͍ ෆ҆ఆͳݪҼ:ϓϩάϥϛϯάݴޠ/SDKͷཧղෆ
ϝΠϯλʔήοτ ϝΠϯλʔήοτͷ ςετλʔήοτ )PTU"QQMJDBUJPOϝΠϯλʔήοτ ςετ࣮ߦͰ)PTU"QQMJDBUJPO͋ΔΜͰ ΞϓϦέʔγϣϯ͕ىಈ࣮ߦ͢Δɻ JNQPSU 1BDLBHFͷ ςετλʔήοτ λʔήοτ
JNQPSU 1BDLBHFTXJGU JNQPSU YDXPSLTQBDF ิYDPEFCVJMEͰͷςετͰ͋ΓTXJGUUFTU࣮ߦͰͷςετʹ͍ͭͯͷͰ͋Γ·ͤΜ &NCFEEFE'SBNFXPSL λʔήοτ &NCFEEFE.PEVMFͷ ςετλʔήοτ )PTU"QQMJDBUJPO/POF ςετ࣮ߦͰ)PTU"QQMJDBUJPO͕ͳ͍ͳΒɺ ΞϓϦέʔγϣϯىಈ࣮ߦ͠ͳ͍ɻ JNQPSU
ྫ: XCTestCase.waitϝιουʹΑΓεϨου͕σουϩοΫ͢Δͱ͖͕͋Δ • ͳͥෆ҆ఆ͞ʹͭͳ͕Δͷ͔ • wait Ͱ Task { @MainActor
in } ͷ݁ՌΛͭςετͳ߹ɺwaitϏδʔΣΠτ͞ ͤͯϝΠϯεϨουΛࢭΊ͍ͯΔͨΊɺTaskͷϝΠϯεϨου࣮ߦΛ્͢Δͪ ͔ͨͱͳΓσουϩοΫ • Ͳ͏ղܾ͢Δͷ͔ • ͦͦXcode 14.3ܥͰܯࠂ͞ΕΔ • Swift ConcurrencyΛ͏߹ fulfillment(of:timeout:…) ϝιου͕༻ҙ͞Ε͍ͯΔ ෆ҆ఆͳݪҼ:ϓϩάϥϛϯάݴޠ/SDKͷཧղෆ
XCTest.framwork ͷίʔυGitHubͰެ։͞Ε͍ͯΔͷͰಡΊΘ͔Δͣ
͓ΘΓʹ
͜ͷൃදͰ ؒҧ͍͕͋ͬͨΓ ҙݟ͕͋ͬͨΒͰ͖Δ͚ͩͳΔ͘ Θ͔Γ͘͢ࢦఠ࣭ͯ͠Β͑Δͱ ༗ҙٛͳΓͱΓ͕Ͱ͖ΔͣͰ͢