簡體   English   中英

單元測試 iOS 10 個通知

[英]Unit testing iOS 10 notifications

在我的應用程序中,我希望斷言已以正確的格式添加了通知。 我通常會通過依賴注入來做到這一點,但我想不出一種方法來測試新的UNUserNotificationCenter API。

我開始創建一個模擬 object 來捕獲通知請求:

import Foundation
import UserNotifications

class NotificationCenterMock: UNUserNotificationCenter {
    var request: UNNotificationRequest? = nil
    override func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)? = nil) {
        self.request = request
    }
}

但是, UNUserNotificationCenter沒有可訪問的初始值設定項我無法實例化模擬。

我什至不確定我是否可以通過添加通知請求和獲取當前通知來進行測試,因為測試需要在模擬器上請求許可,這會使測試停止。 目前我已經將通知邏輯重構為一個包裝器,因此我至少可以在我的應用程序中模擬它並手動測試。

我有比手動測試更好的選擇嗎?

您可以為您正在使用的方法創建一個協議,並在 UNUserNotificationCenter 上進行擴展以符合它。 該協議將充當原始 UNUserNotificationCenter 實現和您的模擬對象之間的“橋梁”,以替換其方法實現。

這是我在操場上編寫的示例代碼,並且工作正常:

/* UNUserNotificationCenterProtocol.swift */

// This protocol allows you to use UNUserNotificationCenter, and replace the implementation of its 
// methods in you test classes.
protocol UNUserNotificationCenterProtocol: class {
  // Declare only the methods that you'll be using.
  func add(_ request: UNNotificationRequest,
           withCompletionHandler completionHandler: ((Error?) -> Void)?)
}

// The mock class that you'll be using for your test classes. Replace the method contents with your mock
// objects.
class MockNotificationCenter: UNUserNotificationCenterProtocol {

  var addRequestExpectation: XCTestExpectation?

  func add(_ request: UNNotificationRequest,
           withCompletionHandler completionHandler: ((Error?) -> Void)?) {
    // Do anything you want here for your tests, fulfill the expectation to pass the test.
    addRequestExpectation?.fulfill()
    print("Mock center log")
    completionHandler?(nil)
  }
}

// Must extend UNUserNotificationCenter to conform to this protocol in order to use it in your class.
extension UNUserNotificationCenter: UNUserNotificationCenterProtocol {
// I'm only adding this implementation to show a log message in this example. In order to use the original implementation, don't add it here.
  func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)?) {
    print("Notification center log")
    completionHandler?(nil)
  }
}

/* ExampleClass.swift */

class ExampleClass {

  // Even though the type is UNUserNotificationCenterProtocol, it will take UNUserNotificationCenter type
  // because of the extension above.
  var notificationCenter: UNUserNotificationCenterProtocol = UNUserNotificationCenter.current()

  func doSomething() {
    // Create a request.
    let content = UNNotificationContent()
    let request = UNNotificationRequest(identifier: "Request",
                                           content: content,
                                           trigger: nil)
    notificationCenter.add(request) { (error: Error?) in
      // completion handler code
    }
  }
}

let exampleClass = ExampleClass()
exampleClass.doSomething() // This should log "Notification center log"

EDITED:
/* TestClass.Swift (unit test class) */

class TestClass {
  // Class being tested 
  var exampleClass: ExampleClass!    
  // Create your mock class.
  var mockNotificationCenter = MockNotificationCenter()

  func setUp() {
     super.setUp()
     exampleClass = ExampleClass()
     exampleClass.notificationCenter = mockNotificationCenter 
  }

  func testDoSomething() {
    mockNotificationCenter.addRequestExpectation = expectation(description: "Add request should've been called")
    exampleClass.doSomething()
    waitForExpectations(timeout: 1)
  }
}
// Once you run the test, the expectation will be called and "Mock Center Log" will be printed

請記住,每次使用新方法時,都必須將其添加到協議中,否則編譯器會抱怨。

希望這有幫助!

您可以利用UNUserNotificationCenter ,然后setValue對返回settings

UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { settings in
    let status: UNAuthorizationStatus = .authorized
    settings.setValue(status.rawValue, forKey: "authorizationStatus")
    completionHandler(settings)
})

我設法使用下面的方法模擬UNNotificationSettings

let object = UIView()
let data = try! NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: false)
let coder = try! NSKeyedUnarchiver(forReadingFrom: data)
let setting = UNNotificationSettings(coder: coder)
setting?.setValue(UNAuthorizationStatus.notDetermined.rawValue, forKey: "authorizationStatus")

盡管測試UNUserNotificationCenter是否被調用而不是測試它是否實際工作很可能是正確的(Apple 應該測試它),但您不需要任何權限來安排然后檢查計划的通知。 僅需要實際顯示通知的權限(並且您絕對不會在單元測試中對其進行測試)。

在我的單元測試中,我調用真正的UNUserNotificationCenter實現,然后檢查預定的通知( UNUserNotificationCenter.current().getPendingNotificationRequests ),所有這些都無需任何權限即可運行,並且測試運行速度非常快。 這種方法比已經提出的方法快得多(從這個意義上說,您需要編寫更少的代碼才能進行測試)。

暫無
暫無

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

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