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
Akio Itaya
March 10, 2026
Programming
160
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
79
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
220
Create a website using Spatial Web
akkeylab
0
400
How to build visionOS apps using Windows
akkeylab
0
210
How to build visionOS apps using Persona
akkeylab
1
470
Summary - Introducing enterprise APls for visionOS
akkeylab
0
560
Apple Vision Pro trial session
akkeylab
0
370
Other Decks in Programming
See All in Programming
ハーネスエンジニアリングにどう向き合うか 〜ルールファイルを超えて開発プロセスを設計する〜 / How to approach harness engineering
rkaga
28
20k
Building on Bluesky's AT Protocol with Ruby
mackuba
0
110
Structured Concurrency, Scoped Values and Joiners in the JDK 25 26 27
josepaumard
1
150
AI時代のエンジニアリングの原則 / Engineering Principles in the AI Era
haru860
0
1.2k
継続的な負荷検証を目指して
pyama86
3
1.1k
属人化しないコード品質の作り方_2026.04.07.pdf
muraaano
0
350
〜バイブコーディングを超えて〜 チームで実験し続けたAI駆動開発
tigertora7571
0
200
Agentic Elixir
whatyouhide
0
450
2026-04-15 Spring IO - I Can See Clearly Now
jonatan_ivanov
1
200
書き換えて学ぶTemporal #fukts
pirosikick
2
370
Skillは並べた。動かなかった。契約で繋いだ。— 65個のSkillから、自走する開発サイクルへ
junholee
0
150
なぜあなたのコードには「コシ」がないのか?〜AI時代に問う、最後まで美味しい設計と戦略〜 #phpconkagawa / phpconkagawa2026
shogogg
0
160
Featured
See All Featured
We Are The Robots
honzajavorek
0
230
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.8k
Technical Leadership for Architectural Decision Making
baasie
3
360
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.4k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.2k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.8k
We Have a Design System, Now What?
morganepeng
55
8.1k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
530
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
190
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 !!