iOS, Swift/ArchitecturePattern
Swift ) ReactorKit (2) 테스트 - EEYatHo iOS
EEYatHo
2022. 12. 9. 14:26
반응형
ReactorKit 에서 테스트 대상
3가지로 나눠서 확인할 수 있다
- View -> Reactor
- 유저의 인터렉션이 일어났을 때, Reactor 에게 기대되는 Action 을 넘기는지
- Reactor -> View
- Reactor 에서 도달한 State 값에 따라 View 의 property 들이 기대한대로 설정되는지
- Reactor Processing ( Action - mutation -> State )
- 특정 State에서 특정 Action이 들어왔을 때 State 가 기대한대로 변경되는지
Stub
ReactorKit 은 용이한 Test 를 위해, Reactor 에 Stub 이라는 기능을 제공한다.
아래처럼 Reactor 에서 Stub 기능을 사용할 것인지 설정할 수 있다.
let reactor = MyReactor()
reactor.isStubEnabled = true
Stub 이 활성화된 Reactor 는 mutate(), reduce() 가 실행되지 않는다.
( = View -> Reactor, Reactor -> View 케이스에서만 사용해야 한다. )
Stub 이 활성화된 Reactor 는 아래의 특정 프로퍼티들을 사용할 수 있다.
Stub 에서 제공하는 Property 들
var state: StateRelay<Reactor.State> { get } // State를 간단하게 설정할 수 있음
var action: ActionSubject<Reactor.Action> { get }
var actions: [Reactor.Action] { get } // 어떤 Action들이 들어왔는지 기록
Stub 사용법 예시
// View --Action-> Reactor
func testAction_refresh() {
// 1. prepare a stub reactor
let reactor = MyReactor()
reactor.isStubEnabled = true
// 2. prepare a view with a stub reactor
let view = MyView()
view.reactor = reactor
// 3. send an user interaction programmatically
view.refreshControl.sendActions(for: .valueChanged)
// 4. assert actions
XCTAssertEqual(reactor.stub.actions.last, .refresh)
}
// Reactor --State-> View
func testState_isLoading() {
// 1. prepare a stub reactor
let reactor = MyReactor()
reactor.isStubEnabled = true
// 2. prepare a view with a stub reactor
let view = MyView()
view.reactor = reactor
// 3. set a stub state
reactor.stub.state.value = MyReactor.State(isLoading: true)
// 4. assert view properties
XCTAssertEqual(view.activityIndicator.isAnimating, true)
}
// Reactor Proccesing
func testIsBookmarked() {
let reactor = MyReactor()
reactor.action.onNext(.toggleBookmarked)
XCTAssertEqual(reactor.currentState.isBookmarked, true)
reactor.action.onNext(.toggleBookmarked)
XCTAssertEqual(reactor.currentState.isBookmarked, false)
}
단일 작업에 대해 State 가 여러번 변경되는 경우 테스트 예제 with RxTest
func testIsLoading() {
// given
let scheduler = TestScheduler(initialClock: 0)
let reactor = MyReactor()
let disposeBag = DisposeBag()
// when
scheduler
.createHotObservable([
.next(100, .refresh) // send .refresh at 100 scheduler time
])
.subscribe(reactor.action)
.disposed(by: disposeBag)
// then
let response = scheduler.start(created: 0, subscribed: 0, disposed: 1000) {
reactor.state.map(\.isLoading)
}
XCTAssertEqual(response.events.map(\.value.element), [
false, // initial state
true, // just after .refresh
false // after refreshing
])
}