如何使用 XCTest UI 测试区分 iOS 13 中的标题和静态文本

Posted

技术标签:

【中文标题】如何使用 XCTest UI 测试区分 iOS 13 中的标题和静态文本【英文标题】:How to differentiate between headers and static text in iOS 13 with XCTest UI Tests 【发布时间】:2019-11-06 14:08:22 【问题描述】:

使用最新版本的 Xcode 11 在 ios 13 上运行,我找不到区分 XCTest UI 测试中的标题和静态文本的方法。

如果我使用 Xcode 11 但在 iOS 12 上运行,我仍然可以通过使用 XCTest 过滤 .other 元素类型来找到具有 .header 特征的视图,但在 iOS 13 视图上使用 .header 特征现在只是由带有 XCTest 的 .staticText 元素类型标识,即使您没有在应用程序中设置 .staticText 可访问性特征。

这给我们带来了一个问题,因为我们使用它来确保我们记得在视图上正确设置标题特征以实现可访问性,以及能够确保我们正在查看正确的屏幕测试。

诚然,只能通过.other 找到标题并不好,但至少它是一种将标题与常规文本区分开来的方法。

这里有一些示例代码来解释:

// ViewController.swift

headerLabel.isAccessibilityElement = true // headerLabel is just a UILabel IBOutlet
headerLabel.accessibilityTraits = [.header]
headerLabel.text = "My Header"

// ViewControllerTests.swift

XCTAssertTrue(XCUIApplication().otherElements["My Header"].firstMatch.waitForExistence(timeout: 30)) // This fails on iOS 13 but works on iOS 12 :(
XCTAssertTrue(XCUIApplication().staticTexts["My Header"].firstMatch.waitForExistence(timeout: 30)) // This fails on iOS 12 but works on iOS 13...

如果你在 Xcode 中 po XCUIApplication() 可以看到,在 iOS 13 上,标题现在只是一个 staticText 与其他所有标签相同。

我尝试过组合不同的accessibilityTraits(因为您可以拥有多个),例如:

headerLabel.accessibilityTraits = [.header, .staticText]

但这无济于事。

【问题讨论】:

更烦人的是,表格部分的标题仍然是.other... 这对我来说似乎是 XCUI 中的一个错误,如果我完全隐藏标签以防止可访问性,它仍然会显示在 XCUI 中,即使它不是故意的。 【参考方案1】:

好的,经过几天的调查,我们找到了解决方法。不幸的是,它使用了一个私有 API,但我们并不担心,因为它是用于测试的,并且比我们迄今为止尝试过的任何其他解决方法都要好。

使用私有 API,可以找出 XCUIElement 所代表的视图的底层 UIAccessibilityTraits

var underlyingAccessibilityTraits: UIAccessibilityTraits 
    guard let rawValue = value(forKey: "traits") as? UInt64 else  return [] 
    return UIAccessibilityTraits(rawValue: rawValue)

现在我们有了特征,我们可以像任何其他OptionSet一样查询它们:

element.underlyingAccessibilityTraits.contains(.header)

我们可以使用它来构建我们自己的查询,而不是使用XCUIElementQuery

let allElementsMatchingID = XCUIApplication().descendants(matching: .any).matching(.any, identifier: id) // id is an optional string as an ID, like when using `XCUIApplication().otherElements[id]`
let allHeaders = allElementsMatchingID.allElementsBoundByAccessibilityElement.filter  $0.underlyingAccessibilityTraits.contains(.header) 
let element = allHeaders[index] // index is an int, like when using `element(boundBy: 0`

这样做的一个缺点(除了必须使用私有 API)是,与常规的 XCUIElementQuerys 不同,如果索引超出范围,这将崩溃,但如果您总是期望元素存在,那就是没什么大不了的。

【讨论】:

【参考方案2】:

您可以在标题的accessibilityIdentifier 中添加前缀,以将其与静态文本区分开来,例如:

myHeader.accessibilityIdentifier = "Header" + title

只使用与之前相同的静态文本

myLabel.accessibilityIdentifier = title

【讨论】:

是的,我们也是这么想的,除了我们制作了一个包装 XCUI 的框架,并且有一个 Header 元素,该元素通过 .other 引用标题,因此获取该元素的查询对于任何使用该元素的人都将失败框架,因为它是staticText。这是供参考的框架:github.com/theappbusiness/TABTestKit

以上是关于如何使用 XCTest UI 测试区分 iOS 13 中的标题和静态文本的主要内容,如果未能解决你的问题,请参考以下文章

单元测试不了解 XCTest 期望的异步 UI 代码?

构建 XCTest UI 测试套件的最佳实践是啥?

xcrun 来自 iOS 上的 XCTest

XCTest UI 测试 - 如何锁定屏幕?

如何在 XCTest 框架的 UI 测试中测试数据?

iOS UI 测试:如何查看日历中的约会?