[英]XCTest - Unable to understand / implement expectations in unit tests (to test aysnc code)
(注意 - 我正在為 macOS 開發,所以請......特定於 iOS 的建議對我沒有幫助)
我正在嘗試做的事情:我有一個應用程序組件,它在后台線程上執行一個簡短的任務,然后,如果滿足某些條件,則在主線程上異步發送一個通知。
注意 - 我沒有在我的應用程序代碼中使用 NSNotification 。 我正在使用自己的自定義通知機制。 因此,任何與 NSNotification 相關的解決方案都不適用於我。
我正在為上述應用程序組件編寫單元測試,只是想檢查該通知是否確實已發送。 在執行斷言之前,我的測試必須能夠等待一秒鍾左右才能讓通知時間到達其訂閱者/觀察者。
我希望能夠在我的測試中測試這兩種可能的情況:兩者都是正常情況。
已發送通知。
未發送通知。
在閱讀了幾個文檔和代碼示例數小時后,我不明白如何實現這一目標。
我只想在測試中等待一秒鍾。 真的有這么復雜嗎?
這是需要測試的應用程序組件及其單元測試:
在下面的代碼中,我在哪里放置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.