[swift] RxSwift에서의 단위 테스트 작성 방법

RxSwift는 이벤트 기반 비동기 프로그래밍을 위한 강력한 도구이지만, 이벤트들이 순차적으로 발생하는 비동기 흐름을 테스트하기는 어려울 수 있습니다. 단위 테스트는 개발자들이 코드의 정확성을 확인하고 버그를 신속하게 찾을 수 있는 중요한 도구입니다.

RxSwift에서의 단위 테스트를 작성하는 방법을 알아보겠습니다.

1. 테스트 환경 구성

먼저, 단위 테스트를 작성하기 위해 테스트 환경을 구성해야합니다. 이를 위해 XCTest 프레임워크를 사용할 수 있으며, 이는 Swift 표준 라이브러리에 포함된 단위 테스트 프레임워크입니다.

import RxSwift
import RxTest
import XCTest

class MyViewModelTests: XCTestCase {
    var scheduler: TestScheduler!
    var disposeBag: DisposeBag!
    
    override func setUp() {
        super.setUp()
        
        scheduler = TestScheduler(initialClock: 0)
        disposeBag = DisposeBag()
    }
    
    override func tearDown() {
        scheduler = nil
        disposeBag = nil
        
        super.tearDown()
    }
}

위 예제에서는 RxSwift와 RxTest를 import하고, XCTest의 XCTestCase를 상속받는 MyViewModelTests 클래스를 정의합니다. setUp()과 tearDown() 메서드는 각각 테스트 케이스 실행 전과 후에 호출됩니다. 마지막으로, scheduler와 disposeBag 인스턴스를 초기화해줍니다.

2. 이벤트 시퀀스 테스트

RxSwift에서 가장 많이 사용되는 기능 중 하나는 옵저버블 시퀀스의 이벤트를 테스트하는 것입니다. TestScheduler를 사용하여 가상의 시간을 관리하고, 이벤트 시퀀스를 생성하여 테스트할 수 있습니다.

func testMyObservable() {
    let scheduler = TestScheduler(initialClock: 0)
    let disposeBag = DisposeBag()

    let observable = scheduler.createHotObservable([
        .next(100, "A"),
        .next(200, "B"),
        .next(300, "C"),
        .completed(400)
    ])

    var observerEvents: [Recorded<Event<String>>] = []

    scheduler.scheduleAt(500) {
        let observer = scheduler.createObserver(String.self)
        observable.bind(to: observer).disposed(by: disposeBag)
        observerEvents = observer.events
    }

    scheduler.start()

    XCTAssertEqual(observerEvents, [
        .next(100, "A"),
        .next(200, "B"),
        .next(300, "C"),
        .completed(400)
    ])
}

위 예제에서는 createHotObservable(:) 메서드를 사용하여 가상의 시간에 따라 이벤트 시퀀스를 생성합니다. 그리고 createObserver(:) 메서드를 사용하여 테스트할 옵저버를 생성하고, 옵저버블과 바인딩하여 이벤트를 받아옵니다. 마지막으로, assertEqual을 사용하여 예상한 이벤트 시퀀스와 실제 이벤트 시퀀스를 비교합니다.

3. 스케줄러 테스트

RxSwift에서 스케줄러는 비동기 작업을 처리하는데 사용되는 중요한 개념입니다. 스케줄러를 테스트하기 위해서는 TestScheduler를 사용하여 가상의 시간을 조작해야합니다.

func testMyScheduler() {
    let scheduler = TestScheduler(initialClock: 0)

    var isCompleted = false

    Observable<Int>.interval(.seconds(1), scheduler: scheduler)
        .take(3)
        .subscribe(onCompleted: {
            isCompleted = true
        })
        .disposed(by: disposeBag)

    scheduler.advanceTo(3)

    XCTAssertTrue(isCompleted)
}

위 예제에서는 interval 연산자를 사용하여 1초마다 이벤트를 생성하는 옵저버블을 생성합니다. take 연산자를 사용하여 이벤트를 3번까지만 처리할 수 있도록 합니다. subscribe(onCompleted:) 메서드를 사용하여 옵저버블이 완료되었을 때 호출할 클로저를 등록합니다. 마지막으로, advanceTo 메서드를 사용하여 가상의 시간을 3초로 이동시키고, isCompleted 변수 값을 확인합니다.

4. 에러 핸들링 테스트

RxSwift에서는 에러 핸들링을 위해 catch 연산자를 사용합니다. catch 연산자를 테스트하기 위해서는 TestScheduler의 createHotObservable 메서드를 사용하여 에러 이벤트를 포함하는 시퀀스를 생성해야합니다.

func testMyErrorHandling() {
    let scheduler = TestScheduler(initialClock: 0)

    let observable = scheduler.createHotObservable([
        .next(100, "A"),
        .next(200, "B"),
        .error(300, TestError.test)
    ])

    var error: Error?

    observable
        .catch({ (error: Error) -> Observable<String> in
            return Observable.just("Error occurred: \(error.localizedDescription)")
        })
        .subscribe(onError: { (e: Error) in
            error = e
        })
        .disposed(by: disposeBag)

    scheduler.start()

    XCTAssertEqual((error as? TestError), TestError.test)
}

위 예제에서는 createHotObservable(_:) 메서드를 사용하여 가상의 시간에 따라 이벤트 시퀀스를 생성하고, error 이벤트를 포함시킵니다. catchError 연산자를 사용하여 error 이벤트가 발생했을 때 예외상황을 처리합니다. 이후, subscribe(onError:) 메서드를 사용하여 에러를 핸들링하는 클로저를 등록하고, assertEqual을 사용하여 예상한 에러와 실제 에러를 비교합니다.

결론

RxSwift에서의 단위 테스트는 중요한 역할을 합니다. 앞서 소개한 방법을 사용하여 RxSwift 코드를 테스트하고, 코드의 정확성을 검증할 수 있습니다. 이를 통해 버그를 줄이고 안정성있는 애플리케이션을 개발할 수 있습니다.

더 많은 정보를 원하시면, RxSwift 공식 문서RxTest 공식 문서를 참조하시기 바랍니다.