In my app I wish to assert that notifications have been added in the correct format. I\'d normally do this with dependency injection, but I can\'t think of a way to test the new
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!
Although it's most probably correct to test that UNUserNotificationCenter
is called and not to test that it actually works (Apple should test that), you do not need any permissions to schedule and then check the scheduled notifications. Permissions are only needed to actually display the notification (and you definitely not testing that in your unit tests).
In my unit tests, I call through to real UNUserNotificationCenter
implementation and then check the scheduled notifications (UNUserNotificationCenter.current().getPendingNotificationRequests
) and all of this works without any permissions and the tests run extremely quick. This approach is much faster than the one already proposed (in that sense that you need to write less code to be able to test).