【问题标题】:XCTWaiter().wait with XCTestExpectation and NSPredicate seems to failXCTestExpectation 和 NSPredicate 的 XCTWaiter().wait 似乎失败了
【发布时间】:2021-01-25 10:04:00
【问题描述】:

我正在尝试编写单元测试,我希望我的测试用例等待某个类中的变量发生变化。所以我用谓词创建了一个期望,并等待使用XCTWaiter().wait(for: [expectation], timeout: 2.0) 改变值,我认为这是正确的使用方法。

以下代码按预期工作:

class ExpectationTests: XCTestCase {
var x: Int = 0

private func start() {
    _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
        self.x = 1
    }
}

func test1() {
    let predicate = NSPredicate(format: "x == 1")
    let expectation = XCTNSPredicateExpectation(predicate: predicate, object: self)
    start()
    let result = XCTWaiter().wait(for: [expectation], timeout: 2.0)
    switch result {
    case .completed:    XCTAssertEqual(x, 1)
    case .timedOut:     XCTFail()
    default:            XCTFail()
    }
}

变量 (x) 被 start() 函数设置为 0,然后在 0.5s 后变为 1。谓词等待该 var (x) 更改。这有效:result 设置为 .completed,而 var 实际上设置为 1。是的 :-)

但是,当我要观察的变量不是本地变量,而是在某个类中时,它不再起作用。考虑以下代码片段:

class MyClass: NSObject {
    var y: Int = 0
    
    func start() {
        _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
            self.y = 1
        }
    }
}

func test2() {
    let myClass = MyClass()
    let predicate = NSPredicate(format: "y == 1")
    let expectation = XCTNSPredicateExpectation(predicate: predicate, object: myClass)
    myClass.start()
    let result = XCTWaiter().wait(for: [expectation], timeout: 2.0)
    switch result {
    case .completed:    XCTAssertEqual(myClass.y, 1)
    case .timedOut:     XCTFail()
    default:            XCTFail()
    }
}

它与第一段代码非常相似,但它总是在 2 秒后结束,result.timedOut。我看不出我做错了什么。我使用来自对象myClass 的变量,而不是本地变量和对象'self',我将其传递给期望值。 (类 var myClass.y 实际上在测试结束时设置为 1。)

我尝试将XCTNSPredicateExpectation(predicate:object) 替换为expectation(for:evaluatedWith:handler),但这并没有什么不同。

StackOverflow 上的许多示例都使用谓词检查 XCUIElement 中的 exists。但我不是在测试 UI;我只想检查某个类中的某个 var 是否在超时时间内发生了变化。我不明白为什么这与在 XCUIElement 中检查 var exists 如此不同。

有什么想法吗?!提前谢谢!

【问题讨论】:

  • 试试@objc var y: Int = 0
  • 谢谢@Willeke,这行得通!好吧,至少一堂课。 (我也需要它作为结构,我不能在那里使用'@objc'。)更重要的是,我无法访问具有我想要观察的属性的类,所以我不能只添加'@objc ' 到一个 var 那里。 (我提供的例子只是为了演示问题;实际上,它有点复杂。)无论如何,我仍然不明白问题是什么。在我在 Stack Overflow 上找到的所有示例中,人们都在使用 XCUIElement 类的属性“存在”以及我正在使用的相同代码。那么为什么我的代码不起作用?!
  • NSPredicate(format: "y == 1") 使用 KVC,value(forKey: "y")
  • 嗨@Willeke,谢谢你的建议,但你能详细说明一下吗?我总是收到“对象不符合“y”属性的 KVC 错误消息。您如何建议我更改谓词以使其起作用?
  • 我不熟悉XCTest 框架。我的猜测是NSPredicate(block:) 和/或设置expectation.handler

标签: swift nspredicate xctest expectations xctestexpectation


【解决方案1】:

好吧,感谢@Willeke 为我指明了正确的方向,我确实找到了解决方案,但我不能说我完全理解它...... 这是我的代码现在的样子:

// MARK: - Test 2
    class MyClass: NSObject {
        var y: Int = 0
        
        func start() {
            _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
                self.y = 1
            }
        }
    }
    
    func test2() {
        let myClass = MyClass()
        let predicate = NSPredicate() { any, _ in
//            guard let myClass = any as? MyClass else { return false }
            return myClass.y == 1
        }
        let expectation = XCTNSPredicateExpectation(predicate: predicate, object: myClass)
        myClass.start()
        let result = XCTWaiter().wait(for: [expectation], timeout: 2.0)
        switch result {
        case .completed:    XCTAssertEqual(myClass.y, 1)
        case .timedOut:     XCTFail()
        default:            XCTFail()
        }
    }

我可以使用带有闭包的谓词,它会定期检查 var 是否已更改,如果它具有正确的值则返回 true。 (它大约每秒执行一次。)但是,考虑到expectation(for:evaluatedWith:handler:) 文档中的描述(这是XCTNSPredicateExpectation 的便捷方法),我实际上认为这就是XCTWaiter 的用途:

期望定期评估谓词。当谓词评估为真时,测试满足预期。

所以,我很高兴我可以继续前进,但我仍然不明白为什么这不适用于 NSPredicate(format: "y == 1") 而不是带有闭包的谓词...

【讨论】:

  • NSPredicate(format: "y == 1") 创建一个NSComparisonPredicate。谓词的leftExpressionkey path expressionpredicate.evaluate(with:) 调用 leftExpression.expressionValue(with:context:),后者调用 value(forKey: "y")
猜你喜欢
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 2015-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-31
相关资源
最近更新 更多