2016-11-29 61 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