是否可以使用SpriteKit在应用程序上使用Xcode UI Testing?

时间:2015-12-15 15:51:37

标签: swift sprite-kit accessibility xcode-ui-testing uiaccessibility

我想使用SKSpriteKit对游戏进行UI测试。

由于我的第一次尝试无效,我想知道是否可以使用SpriteKit进行Xcode UI测试。

4 个答案:

答案 0 :(得分:5)

主要思想是为要进行UI测试的元素创建辅助功能材料。这意味着:

  1. 列出场景中包含的所有可访问元素

  2. 为每个元素配置设置,尤其是frame数据。

  3. 一步一步

    这个答案适用于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)
    
        }
    }
    
    1. UIAccessibilityElement
    2. 创建tapMe
    3. 在设备的坐标上计算帧数据。别忘了frame的起源是UIKit的左上角
    4. 设置UIAccessibilityElement
    5. 的数据
    6. 将此UIAccessibilityElement添加到场景中所有可访问元素的列表中。
    7. 现在可以从UI测试角度访问tapMe

      参考

答案 1 :(得分:3)

根据Apple developer forum discussion,目前无法将UITest与SpriteKit集成:

  

这可能目前无法实现,但可能是

更新2017-02-19

根据@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)

2021 年初更新

使用 SpriteKit、SwiftUI App 和 Swift 5.4 测试的更短的解决方案

在使用 XCUI 测试时,早期的方法似乎不再有效。我的应用程序的基础是一个 SwiftUI 应用程序,它有一个 SKScene 作为它的主视图。为了让它工作,最后其实很简单,而且我的工作步骤要少得多。

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 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)
            }
        }
    }
}