Johannes Fahrenkrug
Johannes Fahrenkrug

Reputation: 44720

Unit Testing UNUserNotificationCenterDelegate Methods

I'd like to unit test some UNUserNotificationCenterDelegate methods, especially userNotificationCenter(_, willPresent:, withCompletionHandler:)

To do that, I'd have to create an instance of UNNotification, but that's not really possible because it only has an initWithCoder initializer. What to do?

This is an example of what I'd like to test:

func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
    completionHandler(.sound)
}

Upvotes: 1

Views: 1050

Answers (2)

Bruno Garelli
Bruno Garelli

Reputation: 234

You can use class_createInstance(UNNotification.classForKeyedArchiver() ...)and cast the value as UNNotification

If you want to manipulate its content and date members you can subclass UNNotification and use this same formula «changing class name and cast» to create it, then you override those members- which are open- and return whatever you want

Upvotes: 0

Johannes Fahrenkrug
Johannes Fahrenkrug

Reputation: 44720

Thanks to this README and the private iOS headers here, I came up with this solution:

func testUserNotificationCenterDelegate() throws {
    // Create the notification content
    let notificationContent = UNMutableNotificationContent()
    notificationContent.title = "Test"
    notificationContent.userInfo = ["someKey": "someValue"]
    
    // Create a notification request with the content
    let notificationRequest = UNNotificationRequest(identifier: "test", content: notificationContent, trigger: UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false))
    
    // Use private method to create a UNNotification from the request
    let selector = NSSelectorFromString("notificationWithRequest:date:")
    let unmanaged = UNNotification.perform(selector, with: notificationRequest, with: Date())
    let notification = unmanaged?.takeUnretainedValue() as! UNNotification
    
    // Test the method
    let callbackExpectation = XCTestExpectation(description: "Callback")
    pushService.userNotificationCenter(UNUserNotificationCenter.current(), willPresent: notification) { (options) in
        XCTAssertEqual(options, .sound)
        callbackExpectation.fulfill()
    }
    wait(for: [callbackExpectation], timeout: 2.0)
}

In the code above, pushService is an instance of a class that conforms to the UNUserNotificationCenterDelegate protocol. Elsewhere in my code, I set that instance as the center's delegate: UNUserNotificationCenter.current().delegate = pushService.

Enjoy!

Upvotes: 2

Related Questions