RxTest의 TestScheduler.start()

김세영·2022년 5월 20일
0

기타 정리

목록 보기
4/4

프로젝트 진행 중 테스트 코드를 작성하다가 다음과 같은 코드가 잘 실행되는 것을 보게 되었다.

var scheduler: TestScheduler!
var viewModel: SomeViewModel!

// 예시를 위한 가짜 코드
func test_SomeClass_someMethod_expectedResult() {
    // ...
    let observable = scheduler.createColdObservable(...)
    subscription = observable.bind(onNext: { [weak self] index in
        self?.viewModel.changeState(at: index)
    })

    scheduler.start()
    
    let result = viewModel.indices.value // viewModel.indices -> BehaviorRelay<[Int]>
    // ...
}

예상

마지막 줄의 result의 값을 예상할 수 없다. (scheduler이 비동기로 작동한다고 생각했기 때문)

  1. scheduler.start()를 하면 TestScheduler가 시작된다.
  2. TestSchedulerobservable의 recordedEvent들을 "비동기"로 방출시킨다.
  3. subscription에서 bind(onNext:)에 등록한 클로저가 이벤트 방출 시 마다 실행된다.
  4. "비동기"이므로 let result = viewModel.indices.value줄에서 result의 값이 바뀌었을 수도, 안바뀌었을 수도 있다.

실행

result의 값이 항상 동일하다.
scheduler이 비동기로 작동한다면 result의 값이 항상 동일하게 나올 수 없다.

  • scheduler의 Virtual Time이 아직 1000에 도달하지 못했을 때 let result = ...이 실행될 수 있기 때문

동일하게 나온다는 것은 scheduler가 동기로 작동한다는 뜻
그래서 TestSchduler.start() 메서드를 살펴보았다.

중간의 guard let next = self.findNext() else ... 구문에 중단점을 잡고 테스트 코드를 디버깅해보았다.
observable의 recordedEvent에서의 timenext.time이 일치하는 것을 확인하였고,
next.invoke()가 호출되면 bind(onNext:)의 클로저가 호출된다는 것을 확인하였다.

또한 repeat-while문을 한 번 들어오면 self.findNext()nil을 반환할 때 까지
해당 반복문을 빠져나오지 않는다는 것을 알 수 있었고, 결론적으로 scheduler가 동기로 작동한다는 것을 확인하였다.

결론

scheduler.start()는 scheduler에 등록되어 있는 recordedEvent 리스트를
그 이벤트의 Virtual Time에 맞게 순서대로 실행한다.

이 때 내부적으로 repeat-while문을 통해 recordedEvent의 이벤트를 방출시키고
이벤트가 종료될 때 까지 해당 반복문을 빠져나오지 않기 때문에
let result = viewModel.indices.value에 항상 동일한 값이 들어갈 수 있었던 것이다.

profile
초보 iOS 개발자입니다ㅏ

0개의 댓글