EEYatHo 앱 깎는 이야기

Swift ) ReactorKit (2) 테스트 - EEYatHo iOS 본문

iOS, Swift/ArchitecturePattern

Swift ) ReactorKit (2) 테스트 - EEYatHo iOS

EEYatHo 2022. 12. 9. 14:26
반응형

 

ReactorKit 에서 테스트 대상


3가지로 나눠서 확인할 있다

  1. View -> Reactor
    • 유저의 인터렉션이 일어났을 때, Reactor 에게 기대되는 Action 을 넘기는지
  2. Reactor -> View
    • Reactor 에서 도달한 State 값에 따라 View 의 property 들이 기대한대로 설정되는지
  3. 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
  ])
}
Comments