Unit testing iOS 10 notifications
You can utilize UNUserNotificationCenter
, then setValue
on the returned settings
UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { settings in
let status: UNAuthorizationStatus = .authorized
settings.setValue(status.rawValue, forKey: "authorizationStatus")
completionHandler(settings)
})
You can create a protocol for the methods you are using, and make an extension on UNUserNotificationCenter to conform to it. This protocol would act as a "bridge" between the original UNUserNotificationCenter implementation and your mock object to replace its method implementations.
Here's an example code I wrote in a playground, and works fine:
/* 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
Keep in mind that every time you use a new method, you'll have to add it to the protocol, or the compiler will complain.
Hope this helps!