Written by
doremin
on
on
[iOS] Swift Testing Framework와 RxTest의 스레드 이슈
[iOS] Swift Testing Framework와 RxTest의 스레드 이슈
1. 문제 상황
Swift Testing Framework와 RxTest를 함께 사용하면서 아래와 같은 테스트 코드를 작성했습니다.
@Test("some test")
func test_something() throws {
let scheduler = TestScheduler(initialClock: 0)
let coordinateObservable = scheduler.createHotObservable([
.next(100, 1)
])
// ... 나머지 테스트 코드
}
문제없이 동작할거라 예상했지만, 다음과 같은 에러가 발생했습니다.
Fatal error: Executing on background thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread.
2. 원인 분석
break point를 찍어가며 에러가 발생하는 부분을 추적해보면 다음과 같습니다.
let coordinateObservable = scheduler.createHotObservable([
.next(100, 1)
]) // 에러 발생
public func scheduleAbsoluteVirtual<StateType>(_ state: StateType, time: VirtualTime, action: @escaping (StateType) -> Disposable) -> Disposable {
MainScheduler.ensureExecutingOnScheduler() // 에러 발생
// ...
}
public static func ensureExecutingOnScheduler(errorMessage: String? = nil) {
if !DispatchQueue.isMain {
rxFatalError(errorMessage ?? "Executing on background thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread.")
}
}
이 문제가 발생하는 이유는 Swift Testing Framework가 기본적으로 테스트를 백그라운드 스레드에서 실행하는 반면, TestScheduler는 메인 스레드에서의 실행을 요구하기 때문입니다.
3. 해결 방법
- XCTest를 이용한다. 내가 선택한 방법은 다음과 같다.
@MainActor
func test_repository_returns_stores() async {
// Given
let coordinate = PublishSubject<CLLocationCoordinate2D>()
let expectedResults = makeExpectedStores()
let expectation = expectation(description: "Stores fetched")
mockRepository.stubbedResult = .success(expectedResults)
var receivedStores: [StoreEntity]?
// When
let input = HomeViewModel.Input(coordinate: coordinate)
let output = sut.transform(input: input)
output.stores
.drive(onNext: { stores in
receivedStores = stores
expectation.fulfill()
})
.disposed(by: disposeBag)
coordinate.onNext(makeCoordinate())
// Then
await fulfillment(of: [expectation], timeout: 2.0)
XCTAssertEqual(receivedStores, expectedResults)
}
- async 함수로 선언되었기 때문에 백그라운드 스레드에서 실행될 수 있는 함수를 @MainActor를 통해서 메인 스레드에서 실행되는 것을 보장한다. (driver는 메인 스레드에서 실행되기 때문)
- expectation을 통해서 값이 채워졌을 때 fulfill을 호출한다.
- 기다린다.. 현재 (2024-12.17) 기준으로는 6.8.0이 최신 버전인데 다음 버전에서는 아마 수정되어서 출시가 될 것 같다. (이미 누군가 같은 문제를 겪고 수정함..!)
- https://github.com/ReactiveX/RxSwift/pull/2610
- https://github.com/ReactiveX/RxSwift/issues/2609