В какой ситуации можно было бы использовать ожиданиеForNotification при быстром тестировании
Я немного сбит с толку тем, что/когда делать с expectationForNotification as opposed to
Ожидаемо с описанием ". Я не смог найти быстрых примеров того, когда и что вы делаете с этим звонком.
Я предполагаю, что это возможно для тестирования уведомлений, но похоже, что это может быть просто более удобная оболочка для всего вызова addObserver()
центра уведомлений.
Может ли кто-нибудь дать краткое объяснение того, что он делает, когда его использовать, и, возможно, несколько строк примера кода?
Ответы
Ответ 1
Как вы уже догадались ОжиданиеForNotification - это ожидание удобства для проверки того, было ли уведомление поднято.
Этот тест:
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)
}
можно заменить следующим:
func testItShouldRaiseAPassNotificationV2() {
expectationForNotification("evPassed", object: nil, handler: nil)
NSNotificationCenter.defaultCenter().postNotificationName("evPassed", object: nil)
waitForExpectationsWithTimeout(0.1, handler: nil)
}
Вы можете найти хорошее объяснение в этом номере Objc.io.
Ответ 2
Чтобы понять разницу между expectation(forNotification:, object:, handler:)
и expectation(description:)
, я создал простой подкласс XCTestCase
с Swift 3.
Здесь мы хотим проверить, что a BlockOperation
, который отправляет Notification
, обновляет указанное свойство Int?
нашего класса с запрошенным значением 50.
1. Используя expectation(description:)
с addObserver(_:, selector:, name:, object:)
import XCTest
class AppTests: XCTestCase {
var testExpectation: XCTestExpectation?
var finalAmount: Int?
func testFinalAmount() {
let notificationName = Notification.Name(rawValue: "BlockNotification")
// Set self as an observer
let selector = #selector(updateFrom(notification:))
NotificationCenter.default.addObserver(self, selector: selector, name: notificationName, object: nil)
// Set expectation
testExpectation = expectation(description: "Did finish operation expectation")
// Set and launch operation block and wait for expectations
let operation = BlockOperation(block: {
NotificationCenter.default.post(name: notificationName, object: nil, userInfo: ["amount": 50])
})
operation.start()
waitForExpectations(timeout: 3, handler: nil)
// Asserts
XCTAssertNotNil(finalAmount)
XCTAssertEqual(finalAmount, 50)
}
func updateFrom(notification: Notification) {
if let amount = notification.userInfo?["amount"] as? Int {
self.finalAmount = amount
}
self.testExpectation?.fulfill()
}
}
2. Используя expectation(description:)
с addObserver(forName:, object:, queue:, using:)
import XCTest
class AppTests: XCTestCase {
var finalAmount: Int?
func testFinalAmount() {
let notificationName = Notification.Name(rawValue: "BlockNotification")
// Set expectation
let testExpectation = expectation(description: "Did finish operation expectation")
// Set self as an observer
let handler = { (notification: Notification) -> Void in
if let amount = notification.userInfo?["amount"] as? Int {
self.finalAmount = amount
}
testExpectation.fulfill()
}
NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: nil, using: handler)
// Set and launch operation block and wait for expectations
let operation = BlockOperation(block: {
NotificationCenter.default.post(name: notificationName, object: nil, userInfo: ["amount": 50])
})
operation.start()
waitForExpectations(timeout: 3, handler: nil)
// Asserts
XCTAssertNotNil(finalAmount)
XCTAssertEqual(finalAmount, 50)
}
}
3. Используя expectation(forNotification:, object:, handler:)
import XCTest
class AppTests: XCTestCase {
var finalAmount: Int?
func testFinalAmount() {
let notificationName = Notification.Name(rawValue: "BlockNotification")
// Set expectation
let handler = { (notification: Notification) -> Bool in
if let amount = notification.userInfo?["amount"] as? Int {
self.finalAmount = amount
}
return true
}
expectation(forNotification: notificationName.rawValue, object: nil, handler: handler)
// Set and launch operation block and wait for expectations
let operation = BlockOperation(block: {
NotificationCenter.default.post(name: notificationName, object: nil, userInfo: ["amount": 50])
})
operation.start()
waitForExpectations(timeout: 3, handler: nil)
// Asserts
XCTAssertNotNil(finalAmount)
XCTAssertEqual(finalAmount, 50)
}
}
TL;DR
Использование expectation(forNotification: String, object:, handler:)
вместо expectation(description:)
в нашем тестовом примере дает некоторые преимущества:
- наш тест теперь требует меньше строк кода (31 вместо 35 или 37 строк),
- наш тест больше не требует использования
addObserver(_:, selector:, name:, object:)
с #selector
или addObserver(forName:, object:, queue:, using:)
,
- нашему тесту больше не требуется объявлять экземпляр
XCTestExpectation
как свойство нашего класса или как измененную область нашего тестового метода и отмечать его как выполненный в какой-то момент с помощью fulfill()
.
Ответ 3
Вы не должны зависеть от UIKit NotificationCenter. Сделайте границу своего типа и проверьте, только если ваш тип отправляет команду на нужный объект. Вот пример того, как вы можете заставить NotificationCenter принять ваш код. (Я не могу получить доступ к Xcode прямо сейчас, поэтому у него может быть некоторая опечатка)
protocol NotificationCenterProtocol {
func post(notification: Notification)
}
extension NotificationCenter: NotificationCenterProtocol {}
class SpyNotificationCenter: NotificationCenterProtocol {
var didPostNotification = false
func post(notification: Notification) {
didPostNotification = true
}
}