안녕하세요. 후르륵짭짭입니다.
요즘 사내에서 테스트 코드를 작성하고 있습니다.
그런데 특정 작업 때문에 Rx Observable.create 내부에 Timer가 들어갔는데,
해당 부분은 테스트를 어떻게 해야할지 모르겠더라구요 ㅠ ㅠ
그래서 이번에 하나씩 적어보려고 합니다.
( 그리고 이제 혹시 저작권에 걸릴까봐 대표사진은 이전에 찍었던 풍경 사진을 담아야 할 것 같습니다 ㅎㅎㅎㅎ)
** RxSwift의 테스트의 기본 **
보통 MVVM의 코드 아키텍처를 사용하면 Input , Ouput을 정의합니다.
그리고 ViewController에서 Input을 주고 ViewModel에서 Input에 대한 값을 로직을 통해 결과를 반환하고
ViewController에서 Ouput을 받아서 사용자에게 보여줍니다.
즉, 하나의 Stream(흐름)을 viewController -> ViewModel -> ViewController로 되게 됩니다.
그런데 매번 Input, Output을 정의하는건 알겠는데, 이걸 왜 항상 이렇게 만들어야하지? 생각했습니다.
이게 필요한 이유가 Test를 용이하게 위해 구분 됐다고 생각 됩니다.
Input, OutPut에 대한 정의를 계속 바뀔 수 있습니다. 하지만 필수적은 로직은 통일성을 가져야한다 생각합니다.
그래서 ViewModel에 로직이 들어가고 이 ViewModel을 실제로 테스트하는게 중요합니다.
테스트를 할 때 Mock 데이터를 만들어서 테스트를 주로하지만,
ViewModel 만큼은 실제 사용하는 객체로 테스트 할 필요가 있다 생각합니다.
( 제 개인적 생각 입니다만,,, )
** RxBlocking **
RxBlocking은 쉽게 테스트할 수 있게 만들어진 Rx Testing API 입니다.
정말 쉽게 테스트 할 수 있습니다.
Subscribe 자체가 Blocking이 되는 것이다.
하지만! Observable이나 BehaviorSubject와 같이 기본값이 탑재되어 있는 경우에는 좋게 사용할 수 있지만
위와 같이 4개의 케이스에 대해서는 사용하기 어렵습니다.
1. RxBlocking은 유한한 순서를 가졌기 때문에 마지막과 첫번째 결과에 대해서는 사용할 수 있지만, 여러 순서를 가진 경우는 RxBlocking이 유용하지 않습니다.
2 , 3. 만약에 긴 시간의 Delay가 필요하다면 RxBlocking은 현재 쓰레드를 잠그고 있어서 유용하지 않습니다.
4. 만약 비동기적 작업의 Input이 있다면 RxBlocking은 사용하기 어렵습니다.
즉, Input Output이 모두 Subject로 구성된 경우 테스트로 사용하기 어렵습니다.
그래서 하나의 순서로 제공 되는 Observable 같은 경우 유용하게 사용할 수 있습니다.
** RxTest **
RxTest는 가상의 시간의 스케줄러를 사용한 테스트 방식 입니다.
위의 코드를 보면 일단 테스트용 스케줄러를 통해 결과값의 Observer를 생성해줍니다.
그리고 OutPut 결과를 방금 만든 Observer로 적용 시킵니다.
그리고 해당 Output결과는 event로 결과가 나오고 우리는 그 event 결과를 테스트 하게 됩니다.
이제 Input을 정의 할겁니다.
Input은 ColdObservable로 통해 Input값들을 (시간, 값)으로 Input Subject에 전달해줍니다.
마지막은 스케줄러를 돌려줘서 해당 테스트를 작동 시킵니다.
- RxTest의 ColdObservable과 HotObservable의 자세한 내용
https://minsone.github.io/programming/reactive-swift-unit-test-1
** Timer가 들어간 Observable을 테스트하는 방법 **
self.goInput.flatMap { _ -> Observable<String> in
return self.makeDelay()
}
.bind(to: self.goOutput2)
위와 같이 특정 시간 후에 방출하는 Observable을 사용하고 있을 때, 일반적인 방법으로 RxTest와 RxBlocking으로 테스트 할 수 없습니다.
왜냐하면 Test할 때 Thread와 Observable에서 방출할 때 사용하는 Thread가 일치하지 않기 때문 입니다.
그래서 어떠한 방법을 사용허더라도 값 방출이 되지 않습니다.
따라서 모든 스케줄러를 동일하게 해줄 방법이 필요합니다.
그래서 Timer Observable을 사용하고 scheduler를 주입받는 방식으로 변경해야합니다.
위와 같이 scheduler를 주입받는 방식을 사용하면 RxTest로 Timer가 들어간 Observable를 테스트 할 수 있게 됩니다.
참고 사이트 :
RxSwift 테스트 튜토리올 :
https://www.raywenderlich.com/7408-testing-your-rxswift-code
RxTest 한국어 간단 내용 정리
https://zeddios.tistory.com/996
RxTest 참고 코드 :
https://betterprogramming.pub/rxswift-unit-testing-explained-in-3-minutes-c024b7a26d
http://adamborek.com/rxtests-rxactionsheet/
RxSwift 뱅크샐러드 내용 정리 - 다시 보기 :
https://www.slideshare.net/ssuser750dc6/rxswift-testing-feat-rxblocking-rxtest-192641266
RxSwift 비동기 테스트 :
https://jisoo.net/2018/03/21/iOS-Asynchronous-test.html
https://stackoverflow.com/questions/50217376/rxswift-tests-with-rxblocking-do-not-end
RxSwift Timer Delay 주는 방법 :
https://stackoverflow.com/questions/58691470/how-to-put-delay-in-rxswift
https://leechamin.tistory.com/541
RxSwift 스케줄러 정리 :
'Xcode > Swift - PlayGround' 카테고리의 다른 글
PlayGround) Objective-C (상속, 카테고리, 프로토콜, 구조체) 정리 (1) | 2022.09.26 |
---|---|
PlayGround) Async - Await 경험 정리#1 (0) | 2022.08.13 |
PlayGround) Subscript란? (0) | 2022.03.15 |
PlayGround) Serial에서 Async는 머지? (0) | 2022.02.28 |
PlayGround) RxSwift-Error Handle (0) | 2022.02.07 |
댓글