В какой ситуации можно было бы использовать ожиданиеForNotification в быстром тестировании

Я немного запутался, что и когда делать с expectationForNotification as opposed toexpectationWithDescription`. Мне не удалось найти в Swift четких примеров того, когда и что вы делаете с этим вызовом.

Я предполагаю, что это, возможно, для проверки уведомлений, но похоже, что это может быть просто более удобная оболочка для всего addObserver() вызова центра уведомлений.

Может ли кто-нибудь дать краткое объяснение того, что он делает, когда его использовать, и, возможно, несколько строк примера кода?


person Jeef    schedule 22.04.2015    source источник


Ответы (3)


Как вы уже догадались, expectationForNotification — это удобное ожидание для проверки того, было ли вызвано уведомление.

Этот тест:

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.

person Giordano Scalzo    schedule 22.04.2015
comment
Привет, если я отправляю уведомление с userInfo, как мне проверить? - person wes. i; 03.08.2020

Чтобы понять разницу между expectation(forNotification:, object:, handler:) и expectation(description:), я создал простой подкласс XCTestCase с помощью Swift 3.

Здесь мы хотим проверить, что 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().
person Imanou Petit    schedule 24.08.2016
comment
Невероятный ответ - person Bart van Kuik; 15.07.2020

Вы не должны зависеть от NotificationCenter UIKit. Сделайте границу своего типа и проверьте, только если ваш тип отправляет команду правильному объекту. Вот пример того, как вы можете сделать так, чтобы NotificationCenter адаптировал ваш код. (Сейчас я не могу получить доступ к Xcode, поэтому может быть опечатка)

protocol NotificationCenterProtocol {
  func post(notification: Notification)
}

extension NotificationCenter: NotificationCenterProtocol {}

class SpyNotificationCenter: NotificationCenterProtocol {
  var didPostNotification = false

  func post(notification: Notification) {
    didPostNotification = true
  }

}
person Daniel Carlos    schedule 25.05.2017