【问题标题】:What if XCTestExpectation is unexpected如果 XCTestExpectation 出乎意料怎么办
【发布时间】:2015-12-24 19:03:05
【问题描述】:

我正在用 Swift 编写 XCTest 单元测试。 这个想法是在特定情况下不能调用回调。

所以我做的是

func testThatCallbackIsNotFired() {

    let expectation = expectationWithDescription("A callback is fired")
    // configure an async operation

    asyncOperation.run() { (_) -> () in
        expectation.fulfill()    // if this happens, the test must fail
    }

    waitForExpectationsWithTimeout(1) { (error: NSError?) -> Void in

        // here I expect error to be not nil,
        // which would signalize that expectation is not fulfilled,
        // which is what I expect, because callback mustn't be called
        XCTAssert(error != nil, "A callback mustn't be fired")
    }
}

调用回调时,一切正常:失败并显示消息“不得触发回调”,这正是我所需要的。

但如果期望没有实现,它会失败并说

异步等待失败:超过 1 秒的超时,未实现预期:“回调已触发”。

既然我需要一个未实现的期望,我不希望有一个失败的测试。

您有什么建议可以避免这种情况吗?或者,也许,我可以用不同的方式达到我的目标?谢谢。

【问题讨论】:

  • 听起来您可能需要重新考虑您正在测试的代码。如果代码异步运行,我可能想要在它完成后回调,不管是否有错误。
  • 话虽如此,如果您的目标是仅在出现错误时调用回调并且您的测试只关心错误是否为 nil,并且您正在编写 Swift 代码,那么为什么错误是可选的吗?您可以通过简单地将其设为非可选来确保它在 compile 时永远不会为零。
  • 感谢您的评论。可能你是对的。让我解释一下目的。我正在处理图像缓存。逻辑是:如果图像已经在内存中,则立即返回(不需要回调)。否则 - 您从回调中获取图像(或错误)。有意义吗?
  • 即使它在内存中并且实际上不是异步的,作为用户,我仍然可能更喜欢在回调中获取它。它使使用它更加简单。我将始终在同一个地方获取图像:在回调中。唯一的区别是获取该图像需要多长时间。
  • 话虽如此,我确信 AFNetworking 是一个经过良好测试的库,您可能可以检查他们的单元测试以确定他们正在做什么(如果有的话)来验证只有一个调用了两个块。

标签: swift unit-testing xctest xctestexpectation


【解决方案1】:

使用isInverted 喜欢在这篇文章中https://www.swiftbysundell.com/posts/unit-testing-asynchronous-swift-code

class DebouncerTests: XCTestCase {
    func testPreviousClosureCancelled() {
        let debouncer = Debouncer(delay: 0.25)

        // Expectation for the closure we'e expecting to be cancelled
        let cancelExpectation = expectation(description: "Cancel")
        cancelExpectation.isInverted = true

        // Expectation for the closure we're expecting to be completed
        let completedExpectation = expectation(description: "Completed")

        debouncer.schedule {
            cancelExpectation.fulfill()
        }

        // When we schedule a new closure, the previous one should be cancelled
        debouncer.schedule {
            completedExpectation.fulfill()
        }

        // We add an extra 0.05 seconds to reduce the risk for flakiness
        waitForExpectations(timeout: 0.3, handler: nil)
    }
}

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,我很生气你不能使用处理程序来覆盖 waitForExpectationsWithTimeout 的超时失败。这是我解决它的方法(Swift 2 语法):

    func testThatCallbackIsNotFired() {
        expectationForPredicate(NSPredicate{(_, _) in
            struct Holder {static let startTime = CACurrentMediaTime()}
    
            if checkSomehowThatCallbackFired() {
                XCTFail("Callback fired when it shouldn't have.")
                return true
            }
    
            return Holder.startTime.distanceTo(CACurrentMediaTime()) > 1.0 // or however long you want to wait
            }, evaluatedWithObject: self, handler: nil)
        waitForExpectationsWithTimeout(2.0 /*longer than wait time above*/, handler: nil)
    }
    

    【讨论】:

      猜你喜欢
      • 2011-11-24
      • 2021-03-25
      • 1970-01-01
      • 2014-04-02
      • 2022-08-02
      • 2020-09-26
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多