In What Situation Would One Use Expectationfornotification in Swift Testing

In what situation would one use expectationForNotification in swift testing

As you have already imagined expectationForNotification is a convenience expectation for checking if a notification was raised.

This test:

func testItShouldRaiseAPassNotificationV1() {
let expectation = expectationWithDescription("Notification Raised")
let sub = NSNotificationCenter.defaultCenter().addObserverForName("evPassed", object: nil, queue: nil) { (not) -> Void in
expectation.fulfill()
}
NSNotificationCenter.defaultCenter().postNotificationName("evPassed", object: nil)
waitForExpectationsWithTimeout(0.1, handler: nil)
NSNotificationCenter.defaultCenter().removeObserver(sub)
}

can be replaced by this one:

func testItShouldRaiseAPassNotificationV2() {
expectationForNotification("evPassed", object: nil, handler: nil)
NSNotificationCenter.defaultCenter().postNotificationName("evPassed", object: nil)
waitForExpectationsWithTimeout(0.1, handler: nil)
}

You can find a good explanation in this Objc.io number.

XCTest - Unable to understand / implement expectations in unit tests (to test aysnc code)

Ok, after a lot of trial and error, this works great for me:

Description: I basically created a helper function in my test case class that contains all the boilerplate expectation/wait code. It does the following:

1 - Creates an expectation (i.e. XCTestExpectation) as a formality.

2 - Calls my (arbitrary) test case assertion code (passed in as a closure) on some global queue thread after the intended delay period. Once this assertion code has completed, the expectation is fulfilled (again, a formality).

3 - Waits on the expectation by calling XCTestCase.wait(timeout). This ensures that the main thread / run loop is kept alive while my assertion code completes on this other thread.

Then, in my test case, I simply invoke that helper function, providing it with a wait period and some code to execute (i.e. my assertions).

This way, I have a simple and expressive reusable function that hides all the excessive ugliness of expectations which I never thought necessary in the first place.

I can put this helper in a base class like MyAppTestCase: XCTestCase, so that it is available to all my test case classes.

NOTE - This solution can be enhanced and made even more generic/reusable, but as of now, this is quite sufficient for the purposes of the originally posted problem.

Solution:

class ComponentBeingTested {

func methodBeingTested() {

doSomeWork()
if certainConditionsAreMet {
DispatchQueue.main.async {sendOutNotification()}
}
}
}

...

class UnitTestForComponentBeingTested: XCTestCase {

let objectBeingTested = ComponentBeingTested()

// Helper function that uses expectation/wait to execute arbitrary
// test code (passed in as a closure) after some delay period.
func executeAfter(_ timeSeconds: Double, _ work: (@escaping () -> Void)) {

let theExpectation = expectation(description: "some expectation")

// Execute work() after timeSeconds seconds
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + timeSeconds) {

// The call to work() will execute my test assertions
work()

// As a formality, fulfill the expectation
theExpectation.fulfill()
}

// Wait for (timeSeconds + 1) seconds to give the work() call
// some time to perform the assertions
wait(for: [theExpectation], timeout: timeSeconds + 1)
}

func testMethodBeingTested() {

// Call the code being tested
objectBeingTested.methodBeingTested()

// Call the helper function above, to do the waiting before executing
// the assertions
executeAfter(0.5) {

// Assume the value of notificationSent is computed elsewhere
// and is available to assert at this point
XCTAssertTrue(notificationSent)
}
}
}

How write unit test for receiving nsnotification asynchronous?

You can add an expectation for the notification:

expectationForNotification("BlaBlaNotification", object: nil) { (notification) -> Bool in

// call the method that fetches the data
sut.fetchData()

waitForExpectationsWithTimeout(5, handler: nil)

But personally I would split this is two tests. One for the fetching of the data (tested using a stub) and one for the sending of the notification.

Nothing in return value

use completion because you in asynchronous block. Try this

func fetchUsers(ref:FIRDatabaseReference, completion(users: [Users])) {
var newUsers = [User]()
ref.observeEventType(.Value, withBlock: {snapshot in
for user in snapshot.children {
let users = User(snapshot: user as! FIRDataSnapshot)
newUsers.append(users)
}
print("closure exited. There are \(newUsers.count) Users in newUsers")
completion(users: newUsers)
})

}


Related Topics



Leave a reply



Submit