iOS12中已启用分段控件的Xcode UITesting测试失败

iOS12中已启用分段控件的Xcode UITesting测试失败,ios,xcode,xcode-ui-testing,Ios,Xcode,Xcode Ui Testing,在我将此作为错误报告提交给苹果之前,我想再次确认我没有做一些非常愚蠢的事情 我附上了一个示例项目,演示了这个问题。我有两个分段控件,其中一个控制另一个的启用状态 如果单击“禁用”按钮,它将禁用第二个分段控件 我有一个基本的UI测试,可以复制这种行为,其构造如下: XCTAssertTrue(app.segmentedControls.buttons["Enabled"].exists) XCTAssertTrue(app.segmentedControls.buttons[

在我将此作为错误报告提交给苹果之前,我想再次确认我没有做一些非常愚蠢的事情

我附上了一个示例项目,演示了这个问题。我有两个分段控件,其中一个控制另一个的启用状态

如果单击“禁用”按钮,它将禁用第二个分段控件

我有一个基本的UI测试,可以复制这种行为,其构造如下:

    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].isSelected)
    XCTAssertTrue(app.segmentedControls.buttons["First"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["First"].isSelected)
    XCTAssertFalse(app.segmentedControls.buttons["Second"].isSelected)
    XCTAssertTrue(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)
    app.segmentedControls.buttons["Disabled"].tap()
    XCTAssertFalse(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)`
app.buttons.matching(identifier: "Enable State Selection").element
myUIGroup.accessibilityIdentifier = "Controls"
let controlsView = app.segmentedControls.matching(identifier: "Controls").element
let enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
XCTAssert(enabledButtons.exists)
如果您在iOS 11.4设备上运行此测试,它将按照我认为应该的方式工作,并且成功。但是,如果我切换到iOS 12.1模拟器设备,测试在最后一行失败(检查第二个控件是否启用)。控件的视觉状态会发生应有的变化。我还在运行12.0的实际iPhone设备上进行了同样的测试

在最后一次检查之前,任何试图睡觉的尝试都不会有什么不同


我所做的有什么根本性的错误吗?或者这是我错过的iOS12中的一个bug或更改吗?

我曾经在UI测试中遇到过同样类型的问题,并且我在几乎所有情况下都发现了一种更好的方法,这关键在于所有测试都依赖于可访问性框架。我一直在得到这些奇怪的、看起来有问题的结果

1。更好的调试

使用以下命令时,调试会变得更容易,该命令将显示元素的层次结构:

print("Current App Tree: \(app.debugDescription)")
层次结构将帮助您确保元素确实存在,以及应用程序/设备/模拟器如何看到它

2。使用辅助功能标识符

不要像现在这样查找元素,而是要了解所有测试都是基于可访问性框架构建的。起初,这似乎无关紧要,但我发现它让生活变得轻松多了。当所有元素都有一个可访问性标识符时,框架会更快乐。基本上每个视图都有一个属性:“accessibilityIdentifier”。这将显示在上面列出的应用程序调试中。如果不设置此项,框架将很难找到您的元素。如果您使用的是故事板,可以在“可访问性”下轻松设置。如果使用的是代码,请按如下方式设置属性:

myView.accessibilityIdentifier = "Enable State Selection"
然后在UI测试中,您可以找到如下元素:

    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].isSelected)
    XCTAssertTrue(app.segmentedControls.buttons["First"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["First"].isSelected)
    XCTAssertFalse(app.segmentedControls.buttons["Second"].isSelected)
    XCTAssertTrue(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)
    app.segmentedControls.buttons["Disabled"].tap()
    XCTAssertFalse(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)`
app.buttons.matching(identifier: "Enable State Selection").element
myUIGroup.accessibilityIdentifier = "Controls"
let controlsView = app.segmentedControls.matching(identifier: "Controls").element
let enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
XCTAssert(enabledButtons.exists)
在您的特定情况下,它看起来像是分段控件中涉及的某种UI组的一部分。我会做两步发现,如下所示:

    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].isSelected)
    XCTAssertTrue(app.segmentedControls.buttons["First"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["First"].isSelected)
    XCTAssertFalse(app.segmentedControls.buttons["Second"].isSelected)
    XCTAssertTrue(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)
    app.segmentedControls.buttons["Disabled"].tap()
    XCTAssertFalse(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)`
app.buttons.matching(identifier: "Enable State Selection").element
myUIGroup.accessibilityIdentifier = "Controls"
let controlsView = app.segmentedControls.matching(identifier: "Controls").element
let enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
XCTAssert(enabledButtons.exists)
然后,您可以执行如下两步查找:

    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["Enabled"].isSelected)
    XCTAssertTrue(app.segmentedControls.buttons["First"].exists)
    XCTAssertTrue(app.segmentedControls.buttons["First"].isSelected)
    XCTAssertFalse(app.segmentedControls.buttons["Second"].isSelected)
    XCTAssertTrue(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)
    app.segmentedControls.buttons["Disabled"].tap()
    XCTAssertFalse(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)`
app.buttons.matching(identifier: "Enable State Selection").element
myUIGroup.accessibilityIdentifier = "Controls"
let controlsView = app.segmentedControls.matching(identifier: "Controls").element
let enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
XCTAssert(enabledButtons.exists)
总的来说,我发现您在UI测试类开始时将这些定义为类全局:

let app = XCUIApplication()
lazy var controlsView = app.segmentedControls.matching(identifier: "Controls").element
lazy var enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
然后,在测试函数中,代码非常干净,而且非常简单:

XCTAssert(enabledButtons.exists)

谢谢你的回复,但我不确定在这种情况下有那么简单。虽然我没有为分段控件设置accessibiliyId,但显式设置它不会更改我看到的结果。你是说它没有显示在debugDescription中吗?这是第一步。UI元素可以移动,我不确定查询究竟是如何工作的,但它并不总是一致的。如果添加标识符,它是否与标识符一起显示在debugDescription中?这是第一步。当你调用debugPrint(应用程序)时,分段控件会显示,并且根据是否设置标识符,显示或不显示标识符。我不认为这是UIElement不在视图层次结构中的问题。是的,不幸的是,视图层次结构可能会根据某些条件发生变化,这就是我必须放弃屏幕录制,真正学习框架。这就是为什么标识符很重要,以及为什么在不同的操作系统和设备中可能会看到不同的结果。如果您设置了标识符,那么您可以保证可以找到它。它还有助于拥有多个级别,而不是从应用程序开始每次搜索。您还可以打印出找到的每个元素的调试结果,而不是整个应用程序。打印(((controlsView.debugDescription)”)。我希望有帮助。