簡體   English   中英

XCTest - 無法理解/實現單元測試中的期望(測試 aysnc 代碼)

[英]XCTest - Unable to understand / implement expectations in unit tests (to test aysnc code)

(注意 - 我正在為 macOS 開發,所以請......特定於 iOS 的建議對我沒有幫助)

我正在嘗試做的事情:我有一個應用程序組件,它在后台線程上執行一個簡短的任務,然后,如果滿足某些條件,則在主線程上異步發送一個通知。

注意 - 我沒有在我的應用程序代碼中使用 NSNotification 我正在使用自己的自定義通知機制。 因此,任何與 NSNotification 相關的解決方案都不適用於我。

我正在為上述應用程序組件編寫單元測試,只是想檢查該通知是否確實已發送 在執行斷言之前,我的測試必須能夠等待一秒鍾左右才能讓通知時間到達其訂閱者/觀察者。

我希望能夠在我的測試中測試這兩種可能的情況:兩者都是正常情況。

  • 已發送通知。

  • 未發送通知。

在閱讀了幾個文檔和代碼示例數小時后,我不明白如何實現這一目標。

我只想在測試中等待一秒鍾。 真的有這么復雜嗎?

  • 睡眠()不起作用
  • DispatchQueue.main.asyncAfter(time) 不起作用
  • 定時器不工作

這是需要測試的應用程序組件及其單元測試:

在下面的代碼中,我在哪里放置expectation.fulfill()???

class ComponentBeingTested {

    func methodBeingTested() {

        doSomeWork()
        if certainConditionsAreMet {
             DispatchQueue.main.async {sendOutNotification()}
        }
    }
}

...

class UnitTestForComponentBeingTested: XCTestCase {

    let objectBeingTested = ComponentBeingTested()

    func testMethodBeingTested() {

          let expectation = self.expectation(description: "Notification was sent")

          // Call the code being tested
          objectBeingTested.methodBeingTested()

          // How do I do this with expectations ??? Where does expectation.fulfill() go ?
          waitForOneSecond()

          XCTAssertTrue(notificationSent)      // Assume the value of notificationSent is available

    }
}

這是一種方法

func testMethodBeingTested() {

      // create expectation
      let expectation = self.expectation(description: "Notification was sent")

      // set expectation condition
      var notificationSent = false
      let observer = NotificationCenter.default
            .addObserver(forName: _Your_Notification_Name, object: nil, queue: nil) { _ in
            notificationSent = true
            expectation.fulfill()
        }

      // Call the code being tested
      objectBeingTested.methodBeingTested()

      // wait for expectation
      self.wait(for: [expectation], timeout: 5)

      XCTAssertTrue(notificationSent)
}

查看XCTNSNotificationExpectation ,它會在發布匹配通知時實現。 可以使用不同的初始化程序,具體取決於您希望對實現期望的限制程度。

要檢查通知是否發送,請將預期 object 上的isInverted設置為true

然后只需在測試結束時添加對waitForExpectations(timeout:handler:)的調用。

好的,經過大量的試驗和錯誤,這對我很有用:

描述:我基本上在我的測試用例 class 中創建了一個助手 function ,其中包含所有樣板期望/等待代碼。 它執行以下操作:

1 - 創建一個期望(即 XCTestExpectation)作為一種形式。

2 - 在預期的延遲期之后,在某個全局隊列線程上調用我的(任意)測試用例斷言代碼(作為閉包傳入)。 一旦這個斷言代碼完成,期望就實現了(再次,形式)。

3 - 通過調用 XCTestCase.wait(timeout) 等待期望。 這確保了當我的斷言代碼在另一個線程上完成時主線程/運行循環保持活動狀態。

然后,在我的測試用例中,我簡單地調用該幫助程序 function,為其提供等待時間和一些要執行的代碼(即我的斷言)。

這樣,我就有了一個簡單而富有表現力的可重用 function,它隱藏了我一開始就認為沒有必要的所有過度丑陋的期望。

我可以把這個助手放在一個基本的 class 中,比如 MyAppTestCase: XCTestCase,這樣我的所有測試用例類都可以使用它。

注意 - 這個解決方案可以得到增強,甚至更通用/可重用,但到目前為止,這對於最初發布的問題來說已經足夠了。

解決方案

class ComponentBeingTested {

    func methodBeingTested() {

        doSomeWork()
        if certainConditionsAreMet {
             DispatchQueue.main.async {sendOutNotification()}
        }
    }
}

...

class UnitTestForComponentBeingTested: XCTestCase {

    let objectBeingTested = ComponentBeingTested()

    // Helper function that uses expectation/wait to execute arbitrary
    // test code (passed in as a closure) after some delay period.
    func executeAfter(_ timeSeconds: Double, _ work: (@escaping () -> Void)) {

        let theExpectation = expectation(description: "some expectation")

        // Execute work() after timeSeconds seconds
        DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + timeSeconds) {

            // The call to work() will execute my test assertions
            work()

            // As a formality, fulfill the expectation
            theExpectation.fulfill()
        }

        // Wait for (timeSeconds + 1) seconds to give the work() call 
        // some time to perform the assertions
        wait(for: [theExpectation], timeout: timeSeconds + 1)
    }

    func testMethodBeingTested() {

          // Call the code being tested
          objectBeingTested.methodBeingTested()

          // Call the helper function above, to do the waiting before executing
          // the assertions
          executeAfter(0.5) {

              // Assume the value of notificationSent is computed elsewhere
              // and is available to assert at this point
              XCTAssertTrue(notificationSent)      
          }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM