In this article, we will see how we can test code that post notifications. Let’s see our example code:
1 2 3 4 5 6 7 8 9 10 11 |
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.post(Notification(name: Notification.Name("aNotification"), object: nil, userInfo: ["key": "value"])) } } |
The code in its current state, is not testable. So before we start writing our unit tests we have to bring the code in a testable state. We notice that we are calling the NotificationCenter in the viewDidLoad. Our object depends on a concrete implementation. Changing from NotificationCenter to another implementation is not straightforward. In this case, we have to stop thinking of concrete implementations but to think of what is the purpose of the NotificationCenter in our object. We are using it because we want to post a notification. By having this in mind we can capture this intent a protocol:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
protocol NotificationCenterPostProtocol { func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]?) } extension NotificationCenter: NotificationCenterPostProtocol {} class ViewController: UIViewController { var notificationCenter: NotificationCenterPostProtocol? = NotificationCenter.default //The object which is responsible for creating the viewController should set the NotificationCenter.default. override func viewDidLoad() { super.viewDidLoad() notificationCenter?.post(name: Notification.Name("aNotification"), object: nil, userInfo: ["key": "value"]) } } |
The trick here is that we want our protocol to have the same method signatures as the NotificationCenter. Having the same method signatures makes the NotificationCenter to conform to this functionality efortless.
Now our code is in a testable, so we can write our test. We will use a spy to capture the call in the post method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import XCTest @testable import DemoUnitTestPostNotification class ViewControllerTests: XCTestCase { func test_viewDidLoad_postNotification() { let sut = ViewController() let spyNotificationCenterPost = SpyNotificationCenterPost() sut.notificationCenter = spyNotificationCenterPost _ = sut.view XCTAssertEqual(Notification.Name("aNotification"), spyNotificationCenterPost.notificationName) XCTAssertNil(spyNotificationCenterPost.object) XCTAssertEqual(["key": "value"], spyNotificationCenterPost.userInfo as! [String : String]) } } extension ViewControllerTests { class SpyNotificationCenterPost: NotificationCenterPostProtocol { var notificationName: NSNotification.Name? var object: Any? var userInfo: [AnyHashable : Any]? func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]?) { self.notificationName = aName self.object = anObject self.userInfo = aUserInfo } } } |