我想使用SKSpriteKit
对游戏进行UI测试。
由于我的第一次尝试无效,我想知道是否可以使用SpriteKit进行Xcode UI测试。
答案 0 :(得分:5)
主要思想是为要进行UI测试的元素创建辅助功能材料。这意味着:
列出场景中包含的所有可访问元素
为每个元素配置设置,尤其是frame
数据。
这个答案适用于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()
}
涉及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)
}
}
UIAccessibilityElement
tapMe
frame
的起源是UIKit的左上角UIAccessibilityElement
UIAccessibilityElement
添加到场景中所有可访问元素的列表中。现在可以从UI测试角度访问tapMe
。
答案 1 :(得分:3)
根据Apple developer forum discussion,目前无法将UITest与SpriteKit集成:
这可能目前无法实现,但可能是
根据@ChrisLivdahl的评论,这可以通过使用UIAccessibility - Session 406, UI Testing in Xcode, WWDC 2015来实现。
这个想法是让元素需要UI Testable。
答案 2 :(得分:1)
我已根据您的(@ Domsware&#39; s)真实答案实施了example project,并且我已经确认此技巧适用于Xcode UI Testing Framework和{{ 3}}
希望此示例有助于对此主题感兴趣的任何人:)
答案 3 :(得分:1)
使用 SpriteKit、SwiftUI App 和 Swift 5.4 测试的更短的解决方案
在使用 XCUI
测试时,早期的方法似乎不再有效。我的应用程序的基础是一个 SwiftUI 应用程序,它有一个 SKScene
作为它的主视图。为了让它工作,最后其实很简单,而且我的工作步骤要少得多。
1.通过仅向 didMove()
方法
override func didMove(to view: SKView) {
isAccessibilityElement = false
}
中所述
2.使您的节点符合 UIAccessibilityIdentification 协议
class YourNode: SKSpriteNode, UIAccessibilityIdentification {
var accessibilityIdentifier: String?
//...
}
提到here
任何需要被 UI Test 访问的节点都需要符合这个协议。 更新扩展,而不是对我现在使用的每个节点进行子类化,使用下面的扩展。
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"]
您可以设置像 .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)
}
}
}
}