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
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
92
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
不幸な GC
chencmd
0
870
ソフトウェアラスタライザ
fadis
1
780
AI時代に学ぶ 好きなルール 嫌いなルール Linter編
shorty5121
0
810
Gmail/Google DriveをトリガーにAIエージェントを動かそう! / Run AI agents with Gmail/Google Drive as triggers!
har1101
3
460
初めての模倣学習とVLA
natsutan
0
460
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
970
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
0
170
バグを直したら useEffect が消えた
colorful12
3
830
Building an Out-of-Order CPU
latte72
0
650
KotlinConf Extended South Korea 2026 Keynote
l2hyunwoo
0
130
DynamoDBの基礎を振り返りながらベクトル検索機能を理解する
musan
3
260
自分的「カンファレンスの楽しみ方」
syumai
0
190
Featured
See All Featured
Art, The Web, and Tiny UX
lynnandtonic
304
22k
The Language of Interfaces
destraynor
162
27k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.7k
Unsuck your backbone
ammeep
672
58k
Amusing Abliteration
ianozsvald
1
290
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
770
KATA
mclloyd
PRO
35
15k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.3k
Ethics towards AI in product and experience design
skipperchong
2
360
Are puppies a ranking factor?
jonoalderson
2
3.9k
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 !!