Edited at

iOSで非同期処理のテスト: XCTestExpectationの設定値と使い方まとめ

More than 1 year has passed since last update.


XCTestExpectationの基本の使い方

XCTestExpectationXCTest フレームワークで非同期処理をテストする際に用いられます。基本の使い方は以下の通り、非同期処理を行う関数のコールバック内で fulfill() を呼び出し、 wait(for:timeout:) することで、待機するexpectationとそれのタイムアウトを設定します。

func someAsynchronousFunction(completion: () -> ()) {

// do some asynchronous tasks
completion()
}

func testHoge() {
let exp = expectation(description: "Hoge")

someAsynchronousFunction {
// callback
exp.fulfill()
}

wait(for: [exp], timeout: 10.0)
}


fulfill()する回数を指定する

wait(for:timeout:)XCTestExpectation のインスタンスをwaitした場合、 デフォルトで fulfill() をテスト終了までに一回呼び出さなければなりません。この時、呼び出しが0回でも、2回以上でもテストが失敗します。

// 呼び出し回数が0回で失敗(タイムアウト)

func testHoge() {
let exp = expectation(description: "Hoge")

someAsynchronousFunction {
// callback
}

wait(for: [exp], timeout: 10.0) // <- Asynchronous wait failed: Exceeded timeout of 10 seconds, with unfulfilled expectations: "Hoge".
}

// 呼び出し回数が2回で失敗(複数回fulfillが呼ばれている)

func testHoge() {
let exp = expectation(description: "Hoge")

someAsynchronousFunction {
// callback
exp.fulfill()
exp.fulfill()
}

wait(for: [exp], timeout: 10.0) // <- caught "NSInternalInconsistencyException", "API violation - multiple calls made to -[XCTestExpectation fulfill] for Hoge."
}

もしも複数回の fulfill() 呼び出しを期待するテストが書きたい場合は、 expectedFulfillmentCount プロパティに期待する呼び出し回数を指定します。

// 呼び出し回数を2回で指定して成功する

func testHoge() {
let exp = expectation(description: "Hoge")
exp.expectedFulfillmentCount = 2

someAsynchronousFunction {
// callback
exp.fulfill()
exp.fulfill()
}

wait(for: [exp], timeout: 10.0)
}

expectedFulfillmentCount プロパティはデフォルトの値が1です。もしも指定した回数を下回るか、超えてしまう場合はテストが失敗します。


fulfill()の呼び出し回数の上限を無くす

expectedFulfillmentCount で指定した回数を超えて fulfill() を呼び出すと、テストが失敗してしまうのは先述した通りです。この呼び出し回数の上限を無くすには assertForOverFulfill プロパティをfalseにします。

// 呼び出し回数が指定回数を超えても成功する

func testHoge() {
let exp = expectation(description: "Hoge")
exp.expectedFulfillmentCount = 2
exp.assertForOverFulfill = false

someAsynchronousFunction {
// callback
exp.fulfill()
exp.fulfill()
exp.fulfill() // <- expectedFulfillmentCountは2だが、3回目のfulfill()を呼んでもテストは失敗しない
}

wait(for: [exp], timeout: 10.0)
}

assertForOverFulfill プロパティはデフォルトではtrueになっています。


期待する結果を反転させる

XCTestExpectation で期待する結果を反転させるには、 isInverted プロパティをtrueにします。デフォルトはfalseです。

XCTestExpectation のインスタンスをwaitして、waitしている間に fulfill() されればテストが成功するというのが通常の挙動です。期待する結果を反転させるというのは、 fulfill() された場合、テストが失敗するということです。

isInverted プロパティがtrueで、 fulfill() を呼び出してテストが失敗した場合、 XCTWaiterDelegate のメソッドが呼び出されます。( waiter(_ waiter:didFulfillInvertedExpectation expectation:) )

// 期待する結果を反転させたexpをfulfill()して失敗する

func testHoge() {
let exp = expectation(description: "Hoge")

someAsynchronousFunction {
exp.isInverted = true
exp.fulfill() // <- Fulfilled inverted expectation "Hoge".
}

wait(for: [exp], timeout: 10.0)
}

isInverted プロパティをtrueにした XCTestExpectation のインスタンスをwaitして、テストがタイムアウトした場合、テストは成功します。このとき、 XCTWaiterDelegate のタイムアウト失敗時のdelegateメソッドは呼び出されません。( waiter(_ waiter:didTimeoutWithUnfulfilledExpectations unfulfilledExpectations:) )

// isInverted = trueとなっているので、タイムアウトしてもテストが成功する

func testHoge() {
let exp = expectation(description: "Hoge")

someAsynchronousFunction {
exp.isInverted = true
}

wait(for: [exp], timeout: 10.0)
}