yujiro's blog

エンジニアリング全般の事書きます

iOS でRxSwift を使ったユニットテストを書く

iOS でRxSwift を使っている場合、ユニットテストはexpectation を使って非同期部分をテストする方法があります。

以下は簡単なサンプルです。

import RxSwift
import XCTest

class MyRxTests3: XCTestCase {
    private let disposeBag = DisposeBag()

    func testSample1() {
        let expectation = self.expectation(description: "非同期")
        var singleValue = ""
        self.getSingle()
            .subscribe(onSuccess: { value in
                singleValue = value
                expectation.fulfill()
            })
            .disposed(by: self.disposeBag)

        self.wait(for: [expectation], timeout: 1.0)
        XCTAssertEqual(singleValue, "Hoge")
    }

    private func getSingle() -> Single<String> {
        return Single.create { subscriber in
            subscriber(.success("Hoge"))
            return Disposables.create()
        }
    }
}

これは冗長で、RxBlocking を使うと綺麗にかけます。

import RxBlocking
import RxSwift
import XCTest

class MyRxTests3: XCTestCase {
    private let disposeBag = DisposeBag()

    func testSample2() {
        let singleValue = try! self.getSingle().toBlocking().single()
        XCTAssertEqual(singleValue, "Hoge")
    }

    private func getSingle() -> Single<String> {
        return Single.create { subscriber in
            subscriber(.success("Hoge"))
            return Disposables.create()
        }
    }
}

.toBlocking でBlockingObservableに変換されます。 BlockingObservableはsuccess イベントが実行されるまで、メインスレッドをブロックし処理を待つので、 .subscribe で購読する必要がなくなります。

ちなみにCompletable の場合は toBlocking().materialize() です

let completable = Completable.create { subscriber in
  subscriber(.completed)
  return Disposables.create()
}

let result = completable
    .toBlocking()
    .materialize()

switch result {
case .completed:
    // ... 
default:
    // ... XCFailとか
}