Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unit test for NSNotification

Tags:

I am working in swift, I want to refresh a page so I am sending it using notification, I am posting a notification in one ViewController and adding observer in another and it is working perfectly. What I want to do is add unit test to it in swift. I checked many sites but was not able to do it. I am new to swift and don't know where to start.

Basically the working is, when i click the button notification is posted and when the next view controller is loaded the notification observer is added.

How can I do the unit testing

Thanks in advance

Edit: Code

NSNotificationCenter.defaultCenter().postNotificationName("notificationName", object: nil) 

and adding observer as

NSNotificationCenter.defaultCenter().addObserver(self, selector: "vvv:",name:"notificationName", object: nil) 
like image 779
user2413621 Avatar asked Feb 03 '15 06:02

user2413621


People also ask

How do you write a unit test?

A typical unit test contains 3 phases: First, it initializes a small piece of an application it wants to test (also known as the system under test, or SUT), then it applies some stimulus to the system under test (usually by calling a method on it), and finally, it observes the resulting behavior.

How do you unit test your application?

Unit tests can be performed manually or automated. Those employing a manual method may have an instinctual document made detailing each step in the process; however, automated testing is the more common method to unit tests. Automated approaches commonly use a testing framework to develop test cases.

What should I write unit tests for?

For Test-Driven Development (TDD), you write unit tests before writing any implementation. This makes your implementation details in your code shorter and easier to understand. In this instance, the best time to write unit tests is immediately. For others, most developers write unit tests after the code's been written.


2 Answers

XCTest has a class specifically for testing Notifications: XCTNSNotificationExpectation. You create one of these expectations, and it's fulfilled when a notification is received. You'd use it like:

// MyClass.swift extension Notification.Name {     static var MyNotification = Notification.Name("com.MyCompany.MyApp.MyNotification") }  class MyClass {     func sendNotification() {         NotificationCenter.default.post(name: .MyNotification,                                       object: self,                                     userInfo: nil)     } }   // MyClassTests.swift class MyClassTests: XCTestCase {     let classUnderTest = MyClass()      func testNotification() {         let notificationExpectation = expectation(forNotification: .MyNotification,                                                             object: classUnderTest,                                                            handler: nil)          classUnderTest.sendNotification()          waitForExpectations(timeout: 5, handler: nil)     } } 

XCTestCase's expectation(forNotification:object:handler:) is a convenience method to create an instance of XCTNSNotificationExpectation, but for more control, you could instantiate one and configure it yourself. See the docs.

like image 53
zpasternack Avatar answered Sep 24 '22 16:09

zpasternack


The general solution is: Use dependency injection (DI) to make your components unit-testable. You can choose use a DI framework (I don't know if there is any good framework for Swift exists yet) or use native approach (i.e. pass object around)

One possible approach for your problem is to wrap NSNotificationCenter to make it mockable/injectable.

This is just a basic idea how you can decouple dependencies. Please don't just copy & paste the code below and expect it to work without understanding it.

import Foundation  protocol NotificationCenter {     func postNotificationName(name: String, object: AnyObject?)      // you can make it take the arguments as NSNotificationCenter.addObserver     func addObserver(callback: AnyObject? -> Void) }  class MyNotificationCenter : NotificationCenter {     var _notificationCenter: NSNotificationCenter      init(_ center: NSNotificationCenter) {         _notificationCenter = center     }      func postNotificationName(name: String, object: AnyObject?) {         // call NSNotificationCenter.postNotificationName     }      func addObserver(callback: AnyObject? -> Void) {         // call NSNotificationCenter.addObserver     } }  class MockNotificationCenter : NotificationCenter {     var postedNotifications: [(String, AnyObject?)] = []     var observers: [AnyObject? -> Void] = []      func postNotificationName(name: String, object: AnyObject?) {         postedNotifications.append((name, object))     }      func addObserver(callback: AnyObject? -> Void) {         observers.append(callback)     } }  class MyView {     var notificationCenter: NotificationCenter      init(notificationCenter: NotificationCenter) {         self.notificationCenter = notificationCenter     }      func handleAction() {         self.notificationCenter.postNotificationName("name", object: nil)     } }  class MyController {     var notificationCenter: NotificationCenter      init(notificationCenter: NotificationCenter) {         self.notificationCenter = notificationCenter     }      func viewDidLoad() {         self.notificationCenter.addObserver {             println($0)         }     } } 

// production code // in AppDeletate.applicationDidFinishLaunching let notificationCenter = MyNotificationCenter(NSNotificationCenter.defaultCenter())  // pass it to your root view controller let rootViewController = RootViewController(notificationCenter: notificationCenter) // or rootViewController.notificationCenter = notificationCenter  // in controller viewDidLoad self.myView.notificationCenter = self.notificationCenter  // when you need to create controller // pass notificationCenter to it let controller = MyController(notificationCenter: notificationCenter)  // in unit test  func testMyView() {     let notificationCenter = MockNotificationCenter()     let myView = MyView(notificationCenter: notificationCenter)     // do something with myView, assert correct notification is posted     // by checking notificationCenter.postedNotifications }  func testMyController() {     let notificationCenter = MockNotificationCenter()     let myController = MyController(notificationCenter: notificationCenter)     // assert notificationCenter.observers is not empty     // call it and assert correct action is performed } 
like image 35
Bryan Chen Avatar answered Sep 24 '22 16:09

Bryan Chen