Нужно спать для TestScheduler, чтобы закончить - PullRequest
0 голосов
/ 28 января 2019

Сводка

Я создаю Observable, который генерирует события, вызывая onNext из DispatchQueue.async, и мой соответствующий модульный тест должен sleep(...), чтобы фактически получить события, даже если я используюTestScheduler.

Подробный вопрос

Я создаю следующий Observable, который читает события из потока gRPC.Я считаю, что gRPC здесь не так важен: просто отметьте, что я call.receive() в цикле while, и передайте его в onNext.

private func createPositionObservable() -> Observable<Position> {
    return Observable.create { observer in
        let request = DronecodeSdk_Rpc_Telemetry_SubscribePositionRequest()

        do {
            let call = try self.service.subscribePosition(request, completion: { (callResult) in
                if callResult.statusCode == .ok || callResult.statusCode == .cancelled {
                    observer.onCompleted()
                } else {
                    observer.onError(RuntimeTelemetryError(callResult.statusMessage!))
                }   
            })  

            DispatchQueue.init(label: "DronecodePositionReceiver").async {
                while let responseOptional = try? call.receive(), let response = responseOptional {
                    observer.onNext(Position.translateFromRpc(response.position))
                }   
            }   

            return Disposables.create {
                call.cancel()
            }   
        } catch {
            observer.onError(error)
            return Disposables.create()
        }   
    }   
    .subscribeOn(scheduler)
}

Теперь я пытаюсь протестировать этот код с помощьюфункция ниже.Опять же, первый абзац касается только настройки контекста gRPC.Я считаю важным то, что:

  1. Я использую TestScheduler
  2. Я передаю этот планировщик в gRPC (scheduler передается в Telemetry и используется какsubscribeOn(scheduler) выше)
  3. I sleep(2) до подтверждения
func checkPositionObservableReceivesEvents(positions: [DronecodeSdk_Rpc_Telemetry_Position]) {
    let fakeService = DronecodeSdk_Rpc_Telemetry_TelemetryServiceServiceTestStub()
    let fakeCall = DronecodeSdk_Rpc_Telemetry_TelemetryServiceSubscribePositionCallTestStub()
    fakeCall.outputs.append(contentsOf: positions.map{ position in createPositionResponse(position: position) })
    fakeService.subscribePositionCalls.append(fakeCall)
    let expectedEvents = positions.map{ position in next(1, translateRPCPosition(positionRPC: position)) }

    let scheduler = TestScheduler(initialClock: 0)
    let observer = scheduler.createObserver(Telemetry.Position.self)
    let telemetry = Telemetry(service: fakeService, scheduler: scheduler)

    let _ = telemetry.position.subscribe(observer)
    scheduler.start()

    sleep(2)

    XCTAssertEqual(expectedEvents.count, observer.events.count)
    XCTAssertTrue(observer.events.elementsEqual(expectedEvents, by: { (observed, expected) in
        observed.value == expected.value
    })) 
}

Если я не sleep(...), мои утверждения не выполняются и observer.events.count не получает события.Такое ощущение, что утверждение произойдет до того, как будут выпущены события.

Как мне с этим справиться?

1 Ответ

0 голосов
/ 28 января 2019

Вы справляетесь с этим, не создавая DispatchQueue внутри вашей функции create.Вместо этого функция должна принять планировщик в качестве параметра, и вы используете планировщик для создания асинхронного блока.

Затем в своем тесте вы передаете планировщик теста.

Примерно так будетсделай это:

let disposable = self.scheduler.schedule(0, action: { _ in
    var cancel = false
    while let responseOptional = try? call.receive(), let response = responseOptional, cancel == false {
        observer.onNext(Position.translateFromRpc(response.position))
    }
    return Disposables.create { cancel = true }
})

return Disposables.create {
    disposable.dispose()
    call.cancel()
}
...