是否可以在使用 SpriteKit 的应用程序上使用 Xcode UI 测试?
Posted
技术标签:
【中文标题】是否可以在使用 SpriteKit 的应用程序上使用 Xcode UI 测试?【英文标题】:Is it possible to use Xcode UI Testing on an app using SpriteKit? 【发布时间】:2015-12-15 15:51:36 【问题描述】:我想对使用 SKSpriteKit
的游戏进行 UI 测试。
由于我的第一次尝试没有成功,我想知道是否可以将 Xcode UI 测试与 SpriteKit 一起使用。
【问题讨论】:
我想知道是否可以通过制作与每个节点相关的 UIAccessibilityElement 对象来对 SpriteKit 节点进行 UI 测试。 @ChrisLivdahl 是的,这是一个有趣的想法。在 WWDC2015 的 Session 406 上,Xcode 中的 UI 测试声明:默认情况下无法访问“图层、精灵和其他图形对象”。所以精灵在默认情况下对 UIAccessibility 是不可见的。我要弄清楚的是如何添加对 UIAccessibility 可见的精灵。 @ChrisLivdahl 我按照你的想法成功地在 UI 测试中访问了 SKSpriteNode。我会在接下来的日子里给出答案。 【参考方案1】:主要思想是为要进行 UI 测试的元素创建可访问性材料。意思是:
列出场景中包含的所有可访问元素
为每个元素配置设置,尤其是frame
data。
一步一步
这个答案是针对 Swift 3 的,主要基于Accessibility (Voice Over) with Sprite Kit
假设我想让名为 tapMe
的 SpriteKit 按钮可访问。
可访问元素列表。
将UIAccessibilityElement
数组添加到场景中。
var accessibleElements: [UIAccessibilityElement] = []
场景的循环寿命
我需要更新两个方法:didMove(to:)
和willMove(from:)
。
override func didMove(to view: SKView)
isAccessibilityElement = false
tapMe.isAccessibilityElement = true
由于场景是可访问性控制器,文档声明它必须将 False
返回到 isAccessibilityElement
。
还有:
override func willMove(from view: SKView)
accessibleElements.removeAll()
覆盖 UIAccessibilityContainer 方法
涉及3种方法:accessibilityElementCount()
、accessibilityElement(at index:)
和index(ofAccessibilityElement
。请允许我介绍一个initAccessibility()
方法,我稍后会介绍。
override func accessibilityElementCount() -> Int
initAccessibility()
return accessibleElements.count
override func accessibilityElement(at index: Int) -> Any?
initAccessibility()
if (index < accessibleElements.count)
return accessibleElements[index]
else
return nil
override func index(ofAccessibilityElement element: Any) -> Int
initAccessibility()
return accessibleElements.index(of: element as! UIAccessibilityElement)!
初始化场景的可访问性
func initAccessibility()
if accessibleElements.count == 0
// 1.
let elementForTapMe = UIAccessibilityElement(accessibilityContainer: self.view!)
// 2.
var frameForTapMe = tapMe.frame
// From Scene to View
frameForTapMe.origin = (view?.convert(frameForTapMe.origin, from: self))!
// Don't forget origins are different for SpriteKit and UIKit:
// - SpriteKit is bottom/left
// - UIKit is top/left
// y
// ┌────┐ ▲
// │ │ │ x
// ◉────┘ └──▶
//
// x
// ◉────┐ ┌──▶
// │ │ │
// └────┘ y ▼
//
// Thus before the following conversion, origin value indicate the bottom/left edge of the frame.
// We then need to move it to top/left by retrieving the height of the frame.
//
frameForTapMe.origin.y = frameForTapMe.origin.y - frameForTapMe.size.height
// 3.
elementForTapMe.accessibilityLabel = "tap Me"
elementForTapMe.accessibilityFrame = frameForTapMe
elementForTapMe.accessibilityTraits = UIAccessibilityTraitButton
// 4.
accessibleElements.append(elementForTapMe)
-
为
tapMe
创建UIAccessibilityElement
计算设备坐标上的帧数据。不要忘记frame
的原点是 UIKit 的左上角
为UIAccessibilityElement
设置数据
将此UIAccessibilityElement
添加到场景中所有可访问元素的列表中。
现在可以从 UI 测试的角度访问 tapMe
。
参考文献
Session 406, UI Testing in Xcode, WWDC 2015
eyes off eyes on — Voiceover accessibility in SpriteKit
How do I support VoiceOver in a SpriteKit game? | Apple Developer Forums
swift - Accessibility (Voice Over) with Sprite Kit - Stack Overflow
【讨论】:
这太棒了!写得也很好! @ChrisLivdahl 谢谢! 我一直在努力让它工作,直到我意识到我的场景的可访问性框架不正确。即使您将场景的 isAccessibilityElement 设置为 false,您仍然需要确保场景的 accessibilityFrame 与视图的边界相匹配。【参考方案2】:根据Apple developer forum discussion,目前无法将 UITest 与 SpriteKit 集成:
这可能目前不可能,但可能是
2017-02-19 更新
根据@ChrisLivdahl 的评论,这可以通过使用 UIAccessibility — Session 406, UI Testing in Xcode, WWDC 2015 来实现。
这个想法是使 UI 所需的元素可测试。
【讨论】:
为了完整起见,您能否添加此论坛讨论的链接? - 没关系 - 找到了!将自己进行编辑...【参考方案3】:我已经根据您 (@Domsware's) 的出色回答实现了 example project,并且我已经确认此技巧对 Xcode UI Testing Framework 和 KIF 都适用。
希望此示例对任何对此主题感兴趣的人有所帮助:)
【讨论】:
【参考方案4】:2021 年初更新
使用 SpriteKit、SwiftUI App 和 Swift 5.4 测试的更短的解决方案
在使用 XCUI
测试时,早期的方法似乎不再适用。我的应用程序的基础是一个以SKScene
作为主视图的 SwiftUI 应用程序。为了让它工作,它实际上非常简单,我工作所需的步骤要少得多。
1.通过在didMove()
方法中仅添加一行来停用场景的可访问性
override func didMove(to view: SKView)
isAccessibilityElement = false
如Dominique Vial's answer中所述
2。使您的节点符合 UIAccessibilityIdentification 协议
class YourNode: SKSpriteNode, UIAccessibilityIdentification
var accessibilityIdentifier: String?
//...
提到here
UI 测试需要访问的任何节点都需要符合此协议。 更新扩展,而不是对我现在使用的每个节点进行子类化。
3.分配accessibilityIdentifier 并激活节点对象的可访问性。
let yourNode = YourNode()
yourNode.isAccessibilityElement = true
yourNode.accessibilityIdentifier = "nodeID"
4.就是这样!运行你的测试!
func testingNodes() throws
app = XCUIApplication()
app.launch()
let node = app.otherElements["nodeID"]
XCTAssert(node.waitForExistence(timeout: 1))
5.可选:设置accessibilityTraits
yourNode.accessibilityTraits = [.button, .updatesFrequently]
let nodeAsButton = app.buttons["nodeID"]
您可以设置特定的 trait,例如 .button
来告诉无障碍 API 您的节点是什么。这对于在测试期间更好地区分您的节点很有用,而且如果您计划为用户实现实际的辅助功能,那么应该正确设置它以使其工作。
更新 1 - 使用扩展:
我现在使用SKNode
上的以下扩展,而不是对节点进行子类化。我现在只设置accessibilityLabel
不再是accessibilityIdentifier
extension SKNode: UIAccessibilityIdentification
public var accessibilityIdentifier: String?
get
super.accessibilityLabel
set(accessibilityIdentifier)
super.accessibilityLabel = accessibilityIdentifier
更新 2 - 使 SKScene 的所有后代都可以访问
为了让节点及其所有后代都可以访问,我最终在 SKNode 上使用了以下扩展。这会将每个节点添加到场景的accessibilityElements
。
extension SKNode: UIAccessibilityIdentification
public var accessibilityIdentifier: String?
get
super.accessibilityLabel
set(accessibilityIdentifier)
super.accessibilityLabel = accessibilityIdentifier
func makeUITestAccessible(label: String, traits: UIAccessibilityTraits)
accessibilityLabel = label
isAccessibilityElement = true
accessibilityTraits = traits
if let scene = scene
if scene.accessibilityElements == nil
scene.accessibilityElements = [self]
else
scene.accessibilityElements?.append(self)
【讨论】:
以上是关于是否可以在使用 SpriteKit 的应用程序上使用 Xcode UI 测试?的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在使用 SpriteKit 的应用程序上使用 Xcode UI 测试?