2016-11-29 62 views
6

在我的應用程序中,我希望聲明已經以正確的格式添加了通知。我通常會使用依賴注入來做到這一點,但我想不出一種方法來測試新的API。單元測試iOS 10通知

我開始創建一個模擬對象,將捕獲的通知要求:

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沒有可訪問的初始化我不能實例模擬。

我甚至不確定我是否可以通過添加通知請求並獲取當前通知來進行測試,因爲測試需要在模擬器上請求允許停止測試的權限。目前我已經將通知邏輯重構爲一個包裝器,所以我至少可以在整個應用程序中對其進行嘲弄並手動測試。

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

回答

7

您可以爲您正在使用的方法創建一個協議,並在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 { 
    func add(_ request: UNNotificationRequest, 
      withCompletionHandler completionHandler: ((Error?) -> Void)?) { 
    // Do anything you want here for your tests 
    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" 

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

class TestClass { 

    // Create your mock class. 
    var notificationCenter: UNUserNotificationCenterProtocol = MockNotificationCenter() 

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

let testClass = TestClass() 
testClass.doSomething() // This should log "Mock center log" 

請記住,每次使用新的方法的時候,你必須將其添加到協議,或編譯器會抱怨。

希望這會有所幫助!

+0

似乎是一個聰明的解決方案,謝謝! – squarefrog

+0

這是一個很好的迴應。你認爲有可能遵循類似的方法來模擬'func getNotificationSettings(completionHandler:@escaping(UNNotificationSettings) - > Swift.Void)'?我無法模擬返回的'UNNotificationSettings'對象,因爲它無法實例化。 – JimmyB