RxTest? RxExpect? 어떻게 사용해야 하는 거죠?
안녕하세요! 10월부터 브런치 iOS 개발자로 합류하게 된 에버입니다!
입사 후 프로젝트에 참여하게 되면서 테스트 코드를 작성할 일이 생겼는데요, 브런치 iOS 프로젝트는 ReactorKit를 사용하고 있었고, 테스트 시 RxTest와 RxExpect를 사용하고 있었습니다. 오늘은 제가 ReactorKit에 대한 테스트 코드를 작성하기 위해 학습하면서 알게 된 두 라이브러리의 사용법에 대해 간단하게 정리해보았습니다.
테스트를 시작하기 전 어떤 것을 테스트할지 먼저 정해야 합니다.
ReactorKit은 단방향으로 사용자의 액션을 받아서 비즈니스 로직(Mutation)을 수행한 후 그 결과에 따라 상태(State)를 변경하게 됩니다. 테스트를 수행하게 된다면 아래의 항목을 테스트할 수 있습니다.
View
Action: 유저의 인터렉션으로부터 적절한 Action이 리엑터에 전달되었는가?
State: State 변화에 따라 View의 프로퍼티가 제대로 세팅되었는가?
Reactor
State: Action이 발생했을 때 비즈니스 로직 실행에 대한 결과로 State가 적절하게 변경되었는가?
이번 글에서는 위의 테스트 대상 중 Reactor에 대한 테스트를 정리해보았습니다.
Reactor 테스트는 검증하고자 하는 State의 값이 바뀌는 횟수가 한 번인지, 여러 번인지에 따라 달라질 수 있습니다. 또한 상황에 따라 전체 Action이 모두 완료된 시점의 State만 검증하고 싶을 수도 있고, State가 변경됐던 모든 과정을 검증하고 싶을 수도 있습니다.
아래 예제 코드처럼 단순히 Action에 대한 결과만 확인하고자 한다면 XCTest에서 제공하는 API만으로도 충분히 검증할 수 있습니다.
그렇다면 Action에 대한 State 변경 사항이 여러 번 있을 경우의 테스트 코드는 어떻게 작성해야 할까요? 바로 이때 RxTest를 사용합니다.
위의 코드를 번호 순서대로 정리해보자면
1. 가상 시간을 사용하는 TestScheduler를 이용하여 RxSwift로 작성되어있는 코드를 테스트할 수 있도록 합니다.
2. 스케쥴러는 구독 여부와 상관없이 정해진 시간에 이벤트를 배출하는 hotObservable를 생성하며, 시퀀스에 대한 이벤트를 정의할 수 있습니다.
3. 시퀀스에 전달되는 이벤트는 Recorded<Event<Element>> 배열 타입으로 아래의 이벤트를 전달할 수 있습니다.
.next - 가상 시간과 배출할 아이템을 전달
.error - 가상 시간과 배출할 에러를 전달
.completed - 가상 시간 전달
4. 구독에 대한 옵저버로 Reactor의 Action을 전달하여 100이라는 가상 시간에 Action.refresh 이벤트를 받도록 합니다.
5. 테스트 스케쥴러를 시작하고 구독 시간 동안 받은 이벤트를 기록한 옵저버를 반환합니다. 이때 기본 생성 시간은 가상 시간 100이며, Subscribe는 200 그리고 Dispose는 1000입니다.
6. response가 수신한 이벤트를 검증합니다. 가상 시간 100때 발생한 refresh Action에 따라 isLoading State가 3가지 상태를 가졌던 것을 확인할 수 있습니다.
위의 6개의 과정을 통해 Action이 일어났을 때 변경되는 State를 검증하는 간단한 코드를 작성해보았는데요, RxExpect를 이용하면 RxExpect 클래스와 input, assert API를 이용하여 더 간결한 코드로 테스트를 진행할 수 있습니다.
그럼 앞서 RxTest로 작성되었던 testIsLoading 코드를 RxExpect로 작성해보겠습니다.
이번에도 순서대로 정리해보자면
1. RxExpect 클래스를 초기화합니다. RxExpect는 내부적으로 TestScheduler를 정의하고 reactor가 테스트하는 동안 Dispose되지 않도록 합니다.
2. input은 RxExpect에서 HotObservable를 생성하고 이벤트 목록을 정의했던 것과 같습니다. 이벤트를 구독하는 옵저버로 reactor.action를 전달합니다.
3. assert는 첫 번째 인자로 받은 reactor.state.map { $0.isLoading } 에 대한 이벤트를 수신하여 클로져로 전달합니다.
4. 전달받은 이벤트에서 element만을 추출 하여 isLoading State를 검증합니다.
여기까지 RxTest와 RxExpect를 이용한 간단한 테스트 코드를 살펴보았습니다. RxTest를 사용할 때는 직접 스케쥴러를 추가하고, HotObservable를 생성하여 이벤트를 추가하고, 리스너를 만들어 이벤트를 기록하고 검증하는 등의 약간은 번거로울 수 있는 작업들을 RxExpect로 간단하게 처리할 수 있었습니다.
처음 입사 후 테스트 코드 한 줄을 작성하는 동안에도, 그리고 지금도 익숙하지 않아 많이 공부하고 있습니다. 이 글을 보시면서 ReactorKit에 테스트 코드를 추가하고자 하는 많은 분께 조금이나마 도움이 됐으면 좋겠습니다.
https://careers.kakao.com/m/jobs/P-12253
- https://github.com/ReactiveX/RxSwift
- https://github.com/ReactorKit/ReactorKit
- https://medium.com/styleshare/reactorkit-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-c7b52fbb131a
- https://github.com/devxoul/RxExpect
- https://brunch.co.kr/@tilltue/18