49
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-05-19

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)
}
49
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?