Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 543 Vote(s) - 3.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to write unit test for NSNotification

#1
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)
Reply

#2
Here is a simpler solution:

**Step 1: Capture the notificationCenter object in an ambiant variable to be able to replace it with some spy class in your unit tests.**

// In your production code:
var notificationCenter = NSNotificationCenter.defaultCenter()

// The code you are testing:
notificationCenter.postNotificationName("notificationName", object: nil)

**Step 2: Define your spy class using inheritance to be able to detect whether the notification was posted or not.**

// In your test code
private class NotificationCenterSpy: NotificationCenter {
var notificationName: String?

override func post(_ notificationName: String, object anObject: Any?)
{
self.notificationName = aName
}
}

**Step 3: replace the ambiant variable in your unit test.**

// In your test code:

// Given
// setup SUT as usual ...
let notificationCenterSpy = NotificationCenterSpy()
sut.notificationCenter = notificationCenterSpy

// When
sut.loadView()

// Then
XCTAssertEqual(notificationCenterSpy.notificationName, "notificationName")

**Step 4: Testing the receiver View Controller**

You should not test whether the receiver View Controller observes the change or not, you should test behaviour.

Something should be happening when the notification is received?
That is what you should be testing, from your test code, post a notification and see if this behaviour happened (in your case if the page gets refreshed).
Reply

#3
[`XCTest`][1] has a class specifically for testing Notifications: [`XCTNSNotificationExpectation`][2]. 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`][3]'s [`expectation(forNotification:object:handler:)`][4] is a convenience method to create an instance of [`XCTNSNotificationExpectation`][2], but for more control, you could instantiate one and configure it yourself. See [the docs][2].


[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[To see links please register here]

[4]:

[To see links please register here]

Reply

#4
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
}
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through