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
150
0
Share
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
Bringing Spatial Web to E-Commerce
akkeylab
0
66
Learn CPU architecture with Assembly
akkeylab
1
2k
Porting a visionOS App to Android XR
akkeylab
0
1.1k
How to handle 3D content on Android XR
akkeylab
0
210
Create a website using Spatial Web
akkeylab
0
390
How to build visionOS apps using Windows
akkeylab
0
200
How to build visionOS apps using Persona
akkeylab
1
450
Summary - Introducing enterprise APls for visionOS
akkeylab
0
550
Apple Vision Pro trial session
akkeylab
0
360
Other Decks in Programming
See All in Programming
20260320登壇資料
pharct
0
160
[PHPerKaigi 2026]PHPerKaigi2025の企画CodeGolfが最高すぎて社内で内製して半年運営して得た内製と運営の知見
ikezoemakoto
0
320
Java 21/25 Virtual Threads 소개
debop
0
320
Laravel Nightwatchの裏側 - Laravel公式Observabilityツールを支える設計と実装
avosalmon
1
310
最初からAWS CDKで技術検証してもいいんじゃない?
akihisaikeda
4
180
脱 雰囲気実装!AgentCoreを良い感じにWEBアプリケーションに組み込むために
takuyay0ne
3
440
AI時代のシステム設計:ドメインモデルで変更しやすさを守る設計戦略
masuda220
PRO
7
1.2k
PHPのバージョンアップ時にも役立ったAST(2026年版)
matsuo_atsushi
0
290
Codex CLIのSubagentsによる並列API実装 / Parallel API Implementation with Codex CLI Subagents
takatty
2
840
Coding as Prompting Since 2025
ragingwind
0
710
AIエージェントで業務改善してみた
taku271
0
440
Going Multiplatform with Your Android App (Android Makers 2026)
zsmb
1
290
Featured
See All Featured
30 Presentation Tips
portentint
PRO
1
270
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
100
Docker and Python
trallard
47
3.8k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
670
Practical Orchestrator
shlominoach
191
11k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
199
73k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
250
Visualization
eitanlees
150
17k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.7k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
170
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 !!