iOSの非同期処理のテストは、 XCTestExpectation クラスのインスタンスをwaitすることで実現できます。以下の記事では XCTestExpectation 側から解説しましたが、今記事では XCTWaiter と XCTWaiterDelegate 側から解説します。
iOSで非同期処理のテスト: XCTestExpectationの設定値と使い方まとめ - Qiita
XCTestExpectationがfulfill()されることを期待する
wait(for:timeout:)
使い方は以下の通り。 XCTestExpectation のインスタンスを、タイムアウト設定とともにwaitし、非同期処理のコールバックで fulfill() されればテストが通ります。
func testHoge() {
let exp = expectation(description: "Hoge")
someAsynchronousFunction {
exp.fulfill()
}
wait(for: [exp], timeout: 10.0)
}
XCTestExpectationがwaitした順にfulfill()されることを期待する
wait(for:timeout:enforceOrder:)
複数の XCTestExpectation インスタンスが、waitした(配列に格納した)順序で fulfill() されることを期待する場合は、このメソッドを使います。引数 enforceOrder: にtrueを渡すと有効化できます。
// 順序を無視してfulfill()したため、テストが失敗する
func testHoge() {
let exp1 = expectation(description: "Hoge")
let exp2 = expectation(description: "Fuga")
someAsynchronousFunction {
exp2.fulfill() // <-
exp1.fulfill() // <- fulfillする順番を1と2で入れ替えれば、テストは成功する
}
wait(for: [exp1, exp2], timeout: 10.0, enforceOrder: true) // <- for: の配列に格納した順序
}
XCTWaiterDelegateの各メソッド
XCTWaiterDelegate には、 XCTestExpectation のwait処理中に発生した例外を処理するdelegateメソッドが存在します。 XCTestCase は XCTWaiterDelegate に準拠しているため、delegateメソッドが呼び出された時の委譲先になります。
各例外と、その例外が発生する条件を紹介していきます。 XCTestExpectation の設定値と使い方の詳細については以下の記事をご覧ください。
iOSで非同期処理のテスト: XCTestExpectationの設定値と使い方まとめ - Qiita
XCTestExpectationがタイムアウトした時に呼ばれるメソッド
XCTestCase のサブクラスでOverrideして使うことができます。
func waiter(_ waiter: XCTWaiter, didTimeoutWithUnfulfilledExpectations unfulfilledExpectations: [XCTestExpectation])
以下のコードで呼び出しが確認できます。 XCTestExpectation のインスタンスを fulfill() せず、設定した時間がきてタイムアウトしてしまうコードです。
// fulfill()されていないのでタイムアウトで失敗する
func testHoge() {
let exp = expectation(description: "Hoge")
someAsynchronousFunction {
// do nothing
}
wait(for: [exp], timeout: 10.0) // <- Asynchronous wait failed: Exceeded timeout of 10 seconds, with unfulfilled expectations: "Hoge".
}
override func waiter(_ waiter: XCTWaiter, didTimeoutWithUnfulfilledExpectations unfulfilledExpectations: [XCTestExpectation]) {
super.waiter(waiter, didTimeoutWithUnfulfilledExpectations: unfulfilledExpectations)
// プリントされる
print("didTimeoutWithUnfulfilledExpectations!!!")
}
期待する結果を反転したXCTestExpectationを fulfill() した時に呼ばれるメソッド
XCTestCase のサブクラスでOverrideして使うことができます。
func waiter(_ waiter: XCTWaiter, didFulfillInvertedExpectation expectation: XCTestExpectation)
以下のコードで呼び出しが確認できます。 XCTestExpectation のインスタンスに isInverted = true を設定することで、期待する結果を反転させることができます。その状態で fulfill() を呼んでしまい、テストが失敗するコードです。
// isInverted = trueになっている状態でfulfill()して失敗する
func testHoge() {
let exp = expectation(description: "Hoge")
exp.isInverted = true
someAsynchronousFunction {
exp.fulfill()
}
wait(for: [exp], timeout: 10.0) // <- Fulfilled inverted expectation "Hoge".
}
override func waiter(_ waiter: XCTWaiter, didFulfillInvertedExpectation expectation: XCTestExpectation) {
super.waiter(waiter, didFulfillInvertedExpectation: expectation)
// プリントされる
print("didFulfillInvertedExpectation!!!")
}
XCTestExpectationをwaitした順序を守らなかった時に呼ばれるメソッド
XCTestCase のサブクラスでOverrideして使うことができます。
func waiter(_ waiter: XCTWaiter, fulfillmentDidViolateOrderingConstraintsFor expectation: XCTestExpectation, requiredExpectation: XCTestExpectation)
以下のコードで呼び出しが確認できます。 wait(for:timeout:enforceOrder:) メソッドを使い、 enforceOrder: true とすることで、引数に渡した配列に格納されている XCTestExpectation のインスタンスが、格納されている順に fulfill() されなかった場合に、例外を発生させることができます。
// 順番通りにfulfill()しなかったために失敗する
func testHoge() {
let exp1 = expectation(description: "Hoge")
let exp2 = expectation(description: "Fuga")
someAsynchronousFunction {
exp2.fulfill()
exp1.fulfill() // <- Failed due to expectation fulfilled in incorrect order.
}
wait(for: [exp1, exp2], timeout: 10.0, enforceOrder: true)
}
override func waiter(_ waiter: XCTWaiter, fulfillmentDidViolateOrderingConstraintsFor expectation: XCTestExpectation, requiredExpectation: XCTestExpectation) {
super.waiter(waiter, fulfillmentDidViolateOrderingConstraintsFor: expectation, requiredExpectation: requiredExpectation)
// プリントされる
print("fulfillmentDidViolateOrderingConstraintsFor!!!")
}
テストの結果を表すXCTWaiter.Result
XCTWaiter には、テストの結果を表す Result というenumが存在します。それぞれのケースが何を示すのか、以下にコメントでまとめました。
extension XCTWaiter {
public enum Result : Int {
// 全てのexpectationが正常にfulfillされた状態
case completed
// expectationが処理される前にタイムアウトされた状態
case timedOut
// expectationが順序通りにfulfillされなかった状態
case incorrectOrder
// 期待する結果が反転されたexpectationをfulfillした状態
case invertedFulfillment
// 他のwaiterに割り込まれた状態
case interrupted
}
}