XCTestでNotificationが呼ばれることを確認する方法
Table of Contents
NotificationCenterへの通知処理を含むメソッドをテストするときは、通知が呼ばれていることを、条件によっては呼ばれないことを確認する必要があります。今回は、そのテストの実装方法を備忘録として書いておきます。
参考資料
- https://stackoverflow.com/questions/52217293/testing-notifications-in-xctests-uitests
- https://medium.com/swift2go/unit-testing-swift-notification-68e307f7d6ae
- https://gist.github.com/fxm90/23dc7debc5ee8245237c08e5af8679bc
- https://qiita.com/mishimay/items/3ac2bd544c677c4de534
テストの実装方法 #
通知が呼ばれていること #
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) // 通知が呼ばれたら落ちる。タイムアウトするのが正常。
これで、通知が呼ばれないことを確認できました。
通知で届くオブジェクトが意図どおりであること #
通知で受け取るオブジェクトが想定どおりであることを確認するには、XCTNSNotificationExpectation
のinit(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
のインスタンスを使用して、completed
・incorrectOrder
・interrupted
・invertedFullfillment
・timedout
の5種類のパターンをチェックすることができます。
通知の順序など細かく検証できそうなので、これが便利な状況もありそうです。
便利な実装方法 #
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
関連のクラスを知ったことで、非同期のテストもかけるようになりました。なんとなく苦手意識のあるユニットテストではありますが、書き方がわかってくると、面白いものです。