すっさんぽ
  1. Posts/

XCTestでNotificationが呼ばれることを確認する方法

·

NotificationCenterへの通知処理を含むメソッドをテストするときは、通知が呼ばれていることを、条件によっては呼ばれないことを確認する必要があります。今回は、そのテストの実装方法を備忘録として書いておきます。

参考資料

テストの実装方法 #

通知が呼ばれていること #

Notificationが呼ばれることを確認するには、XCTNSNotificationExpectationを使用します。 また、通知を待つにはwait(for:timeout:)を使用します。

let name = Notification.Name(rawValue: "testNotification")
let notificationExpectation = XCTNSNotificationExpectation(name: name)

executePostingNotification()
wait(for: [notificationExpectation], timeout: 0.3)

たったこれだけで、ただしく通知が呼ばれていることを確認できます。

通知が呼ばれていないこと #

通知が呼ばれていないことを確認するには、XCTestExpectationの期待を反転させます。

let name = Notification.Name(rawValue: "testNotification")
let notificationExpectation = XCTNSNotificationExpectation(name: name)
notificationExpectation.isInverted = true    // この1行!

executePostingNotification()    // 通知は発生しない条件など
wait(for: [notificationExpectation], timeout: 0.3)    // 通知が呼ばれたら落ちる。タイムアウトするのが正常。

これで、通知が呼ばれないことを確認できました。

通知で届くオブジェクトが意図どおりであること #

通知で受け取るオブジェクトが想定どおりであることを確認するには、XCTNSNotificationExpectationinit(name:object:)を使用します。

let name = Notification.Name(rawValue: "testNotification")
let notificationExpectation = XCTNSNotificationExpectation(name: name, object: 1234)

executePostingNotification()
wait(for: [notificationExpectation], timeout: 0.3)

これで、通知で届くオブジェクトが意図どおりであることを確認できました。型のチェックも自動でやってくれます。

XCTWaiterを使用して待つ #

XCTestCaseのインスタンスメソッドwait(for:timeout:)を使用する他に、XCTWaiterを使用する方法があります。

let result = XCTWaiter().wait(for: [notificationExpectation], timeout: 0.3)
XCTAssertEqual(result, .completed)

この実装方法では、XCTWaiter().wait(for:timeout:)から返されるXCTWaiter.Resultのインスタンスを使用して、completedincorrectOrderinterruptedinvertedFullfillmenttimedoutの5種類のパターンをチェックすることができます。

XCTWaiter.waitから返却されるResultを使用して、XCTAssertEqualの補完を表示したところ

通知の順序など細かく検証できそうなので、これが便利な状況もありそうです。

便利な実装方法 #

XCTestCaseのインスタンスメソッド expectation(forNotification:object:handler:)またはexpectation(forNotification:object:notificationCenter:handler:)を使用すると、インスタンスを引き回す必要がなくなり、記述がシンプルになります。 また、このときはwaitForExpectations(timeout:handler:)を使用して、まとめて通知の検証ができます。

let name = Notification.Name(rawValue: "testNotification")
expectation(forNotification: name,
            object: nil,
            handler: nil)

executePostingNotification()
waitForExpectations(timeout: 0.3, handler: nil)

まとめ #

Notificationが呼ばれていることを確認するための、テストの書き方をまとめました。 普段はXCTAssert〇〇ばかり使用しているので、同期実行のテストしか書けませんでした。 今回、XCTestExpectation関連のクラスを知ったことで、非同期のテストもかけるようになりました。なんとなく苦手意識のあるユニットテストではありますが、書き方がわかってくると、面白いものです。

XCTestExpectation関連のクラス

XCTestExpectation、便利そうだ。