XCUIElement.exists在元素屏幕外时返回false问题求助
问题分析
在Xcode 15的XCUITest中,针对Files应用的屏幕外文件元素,exists属性返回false的情况,确实和常规认知中“只要元素在视图层级就返回true”有出入,但这大概率不是Bug,而是Files应用的UI实现特性导致的。
Files应用的列表采用了复用机制(类似UITableView的cell复用),屏幕外的cell并不会提前加载到视图层级中,只有当滚动到可视区域时才会被实例化。这就导致未进入可视区域的文件元素实际上并不存在于当前的视图层级里,所以exists自然返回false。
解决方案
要检测屏幕外元素的存在,不能依赖exists属性,而是需要结合滚动操作和元素查询的方式来处理,以下是几种可行的方法:
1. 滚动到元素并验证
利用XCUITest的内置滚动方法,尝试滚动到目标元素,若滚动成功则说明元素存在:
let targetElement = app.cells.matching(identifier: fileIdentifier).firstMatch do { try targetElement.scrollTo() // 滚动成功,说明元素存在 XCTAssertTrue(targetElement.exists) } catch { // 滚动失败,元素不存在 XCTAssertFalse(targetElement.exists) }
注意:scrollTo()方法会自动处理滚动逻辑,若元素不存在则会抛出异常,需要捕获处理。
2. 查询列表的总元素数量
如果知道文件列表的容器,可以先获取列表中所有元素的数量,再判断目标元素是否在范围内:
let fileList = app.tables.firstMatch // 假设Files的文件列表是table let totalFiles = fileList.cells.count // 结合元素的位置信息或标识符,判断目标是否存在 let targetExists = fileList.cells.matching(identifier: fileIdentifier).count > 0 XCTAssertTrue(targetExists)
这种方法的前提是列表容器能正确返回所有元素的数量,不过部分复用列表可能只返回当前可视的元素数量,需要测试验证。
3. 模拟滚动并循环查询
手动模拟滚动操作,每次滚动后查询目标元素是否存在,直到元素找到或滚动到列表底部:
let targetElement = app.cells.matching(identifier: fileIdentifier).firstMatch let fileList = app.tables.firstMatch var elementFound = false var scrollCount = 0 let maxScrolls = 10 // 设置最大滚动次数,避免无限循环 while scrollCount < maxScrolls && !elementFound { if targetElement.exists { elementFound = true break } // 模拟向上滚动(根据列表方向调整) fileList.swipeUp() scrollCount += 1 } XCTAssertTrue(elementFound, "目标文件未找到")
这种方法适合无法使用scrollTo()的场景,但需要注意设置滚动次数上限,防止测试陷入死循环。
总结
Xcode 15中exists返回false的情况,本质是Files应用的cell复用机制导致屏幕外元素未被加载到视图层级,不属于XCUITest的Bug。通过滚动验证、数量查询或循环滚动查询的方式,都可以实现对屏幕外元素存在性的检测。
内容的提问来源于stack exchange,提问作者lemon lime pomelo

