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
}
}