branch10480’s blog

Topics that I've learned.

iOSアプリ開発自動テストの教科書 を読んでいく 〜 13 〜

非同期なAPIのテスト - XCTestExpectation

非同期なAPIをテストする場合は、XCTestExpectationを用い、処理が完了するまで待機するのが一般的。

テスト対象のサンプルコード。

func echo(message: String, _ handler: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        // 3秒待機
        Thread.sleep(forTimeInterval: 3)
        
        // 終端に「&」をつけてコールバック呼び出し
        DispatchQueue.main.async {
            handler(message + "&")
        }
    }
}

// echo() の利用
echo(message: "hoge") { message in
    XCTAssertEqual(message, "hoge" + "&") // 3病後に実行される
}

まずは間違ったテスト処理を示す。

func testEcho() {
    echo(message: "Hello") { message in
        XCTAssertEqual(message, "Hello!")
    }
}

この場合、テストは成功してしまう。

なぜ??

それは非同期処理の終了を待たずにテストメソッド自体が終了してしまう、つまりアサーションが実行されないため。

失敗したアサーションがなかったためテストは成功とみなされる。

以下が正しいテストコード!

func testEcho() {

    // 待機用の XCTestExpectation を生成
    let exp: XCTestExpectation = expectation(description: "wait for finish")
    echo(message: "Hello") { message in
        XCTAssertEqual(message, "Hello!")
        
        // expの待機を解除
        exp.fulfill()
    }
    
    // expに対してfulfill()が呼び出されるまで待機(5秒でタイムアウト)
    wait(for: [exp], timeout: 5)
}

> 次回に続く