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
How to stabilize UI tests using XCTest
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Akio Itaya
March 10, 2026
Programming
200
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
How to stabilize UI tests using XCTest
Ebisu.mobile #13 〜STORESのモバイルテストの現在地〜
https://hey.connpass.com/event/382942/
Akio Itaya
March 10, 2026
More Decks by Akio Itaya
See All by Akio Itaya
Building a Meta Ray-Ban display app
akkeylab
0
190
What If Your App Could Debug Itself?
akkeylab
0
95
Bringing Spatial Web to E-Commerce
akkeylab
0
110
Learn CPU architecture with Assembly
akkeylab
1
2.2k
Porting a visionOS App to Android XR
akkeylab
0
1.2k
How to handle 3D content on Android XR
akkeylab
0
260
Create a website using Spatial Web
akkeylab
0
440
How to build visionOS apps using Windows
akkeylab
0
240
How to build visionOS apps using Persona
akkeylab
1
510
Other Decks in Programming
See All in Programming
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
110
tsc.rip を支える技術 / Kyoto.なんか #8
susisu
0
4.3k
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
990
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
110
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
110
Swift愛好会100回記念 第1回を振り返る
jollyjoester
0
120
Deep dive into the select statement (GopherCon UK)
jespino
0
170
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
1
190
Claude Codeを組織的に動かして月400PRを実現した話
happy_ryo
0
260
バグを直したら useEffect が消えた
colorful12
3
840
新卒PdEのリアル
ryu1013
1
410
まずはプロンプトガイドを読もう、話はそれからだ
kiakiraki
1
260
Featured
See All Featured
Utilizing Notion as your number one productivity tool
mfonobong
4
570
RailsConf 2023
tenderlove
30
1.5k
Claude Code のすすめ
schroneko
67
230k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.7k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
290
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
The untapped power of vector embeddings
frankvandijk
2
1.9k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
250
My Coaching Mixtape
mlcsv
0
310
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
440
Six Lessons from altMBA
skipperchong
29
4.5k
GraphQLとの向き合い方2022年版
quramy
50
15k
Transcript
XCTestΛͬͨUIςετͷ҆ఆԽઓུ akkey / @AkkeyLab Ebisu.mobile #13
The Point 2 Wait & Scroll ࣮ߦڥͱίϯςϯπʹґଘ͠ͳ͍ςετΛࢦͯ͠
Wait
Wait 4 let app = XCUIApplication() let button = app.buttons.firstMatch
button.tap()
Wait 5 let app = XCUIApplication() let button = app.buttons.firstMatch
button.tap() ❌
Wait 6 let app = XCUIApplication() let button = app.buttons.firstMatch
button.tap() ❌ ❌
Wait 7 let app = XCUIApplication() let button = app.buttons.firstMatch
button.tap() ✅
Fix
Wait 9 let app = XCUIApplication() let button = app.buttons.firstMatch
let predicate = NSPredicate( format: "exists == true && hittable == true" ) let ex = XCTNSPredicateExpectation( predicate: predicate, object: button ) XCTWaiter.wait( for: [ex], timeout: 10 ) button.tap() ⏰ wait…
Wait 10 let app = XCUIApplication() let button = app.buttons.firstMatch
let predicate = NSPredicate( format: "exists == true && hittable == true" ) let ex = XCTNSPredicateExpectation( predicate: predicate, object: button ) XCTWaiter.wait( for: [ex], timeout: 10 ) button.tap() ⏰ wait…
Wait 11 let app = XCUIApplication() let button = app.buttons.firstMatch
let predicate = NSPredicate( format: "exists == true && hittable == true" ) let ex = XCTNSPredicateExpectation( predicate: predicate, object: button ) XCTWaiter.wait( for: [ex], timeout: 10 ) button.tap() ✅ completed! ✅
Wait 12 let predicate = NSPredicate { object, _ in
guard let element = object as? XCUIElement else { return false } return element.exists && element.isHittable } let ex = XCTNSPredicateExpectation( predicate: predicate, object: button ) XCTWaiter.wait( for: [ex], timeout: 10 ) button.tap() ✅ completed! ✅
Scroll
Scroll 14
Scroll 15
Scroll 16
Scroll 17 scrollView.swipeLeft(velocity: .slow)
Scroll 18 scrollView.swipeLeft(velocity: .slow) ⚠
Fix
let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) let end
= start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) Scroll 20 ☝
Scroll 21 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ☝
Scroll 22 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) ☝ 350 -350
Scroll 23 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ☝
Scroll 24 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ☝ ⏰0.02s
Scroll 25 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ☝
Scroll 26 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ☝ 🚀250px/s -350
Scroll 27 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ☝ ⏰0.07s
Scroll 28 let start = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: -350, dy: 0)) start.press( forDuration: 0.02, thenDragTo: end, withVelocity: 250, thenHoldForDuration: 0.07 ) 350 ⏰0.07s ☝
The Point 29 Wait & Scroll ࣮ߦڥͱίϯςϯπʹґଘ͠ͳ͍ςετΛࢦͯ͠
Thank you !!