apple/UIKit & ReactiveX

[ReactorKit] ReactorKit 공부하기 #2

lgvv 2022. 7. 24. 02:40

ReactorKit 공부하기 #2

 

📌 해당 문서는 ReactorKit 3.2.0을 기준으로 하고 있습니다. 

ReactorKit은 SPM을 지원하지 않습니다. 

해당 프로젝트를 위해 RxTest, RxBlocking을 추가적으로 사용하였습니다.

코드 파일은 글 제일 하단에서 확인하실 수 있습니다.

 

 

이번에는 ReactorKit 테스트에 대해서 알아볼 예정이다.

한 이틀을 환경문제로 삽질을 했는데, 그 해결법을 여기에 담고 있다...

 

✅ RxTest는 꼭 Tests쪽으로 타겟을 잡아주세요!

그렇지 않으면 앱이 죽습니다 ㅠ

 

ㅜㅜㅜ 이걸로 하루 반 삽질,,

 

✅ 1편 포스팅 코드를 그대로 사용했습니다.

 

ReactorKit testing

ReactorKit의 경우에는 테스트를 위한 함수가 내장되어 있다. 손쉽게 view와 reacotr 둘 다 테스트 할 수 있다!!

 

그렇다면 무엇을 테스트 해야할까?

 - View 

   - Action :: 주어진 유저의 상호작용에 따라 적절한 action이 reactor로 보내지는가?

   - State :: 주어진 state에서 view의 변수가 적절하게 set(저장) 되는가?

 - Reactor

   - State :: action에 따라 state가 적절하게 변경되었는가?

 

총 3가지 부분을 테스트하면 된다.

 

테스트 코드 작성과 관련해서는 다른 분 블로그의 댓글로 소통을 했었는데, 그 부분이 궁금하다면 글 제일 하단의 링크를 첨부할테니, 그 부분의 댓글을 확인하기!

✅ 리액터킷 테스트코드

 

//
//  ReactorKitPracticeTests.swift
//  ReactorKitPracticeTests
//
//  Created by Hamlit Jason on 2022/07/23.
//

import XCTest
@testable import ReactorKitPractice
import RxTest
import RxSwift

class ReactorKitPracticeTests: XCTestCase {
    
    override func setUp() {
    }
    
    override func tearDownWithError() throws { }
    
    // MARK: - View -> Reactor
    // View에서 Reactor Action을 잘 넘기는지 테스트
    func test_Action_View_to_Reactor() {
        // given
        let reactor = CounterViewReactor()
        let viewController = CounterViewController()
        reactor.isStubEnabled = true
        viewController.reactor = reactor
        
        // when
        viewController.increaseButton.sendActions(for: .touchUpInside)
        XCTAssertEqual(reactor.stub.actions.last, .increase)
        
        // then
        viewController.decreaseButton.sendActions(for: .touchUpInside)
        XCTAssertEqual(reactor.stub.actions.last, .decrease)
    }
    
    // MARK: - Reactor -> View
    // Reactor에서 변경된 상태(State)가 View에도 정상적으로 반영되는지 확인.
    func test_State_Reactor_to_View() {
        // given
        let reactor = CounterViewReactor()
        let viewController = CounterViewController()
        reactor.isStubEnabled = true
        viewController.reactor = reactor
        
        // when
        reactor.stub.state.value = CounterViewReactor.State(value: 0, isLoading: true)
        
        // then
        XCTAssertEqual(viewController.loadingIndicator.isAnimating, true)
        XCTAssertEqual(viewController.countLabel.text, "0")
    }
    
    // MARK: - Reactor
    // action을 받으면 비지니스 로직(Mutation)이 잘 처리되어 State값이 기대값으로 변하는지 확인.
    func test_Reactor() {
        let reactor = CounterViewReactor()
        
        let expectation = XCTestExpectation()
        expectation.expectedFulfillmentCount = 1
        
        // when
        reactor.action.onNext(.increase)
        
        // then
        XCTAssertEqual(reactor.currentState.isLoading, true)
    }
    
}

 

 

 

 

🚨 실패한 테스트코드

실패한 테스트코드입니다.

isLoading의 경우에는 false -> true(딜레이 1초) -> false로 결과가 방출되어야할 것 같은데, false -> true만 내려오고 있습니다.

딜레이 처리를 잘못한 것이 원인이 아닐까 생각하고 있으나, 현재 저의 시각으로는 문제를 해결하지 못했습니다.

해당 원인은 아시는 분은 알려주세요!

 

   // 실패한 테스트 코드
   // 작성일: 2022년 7월 25일
   // 원인: delay의 문제로 보이나, 어떻게 해결할지 아직 잘 모르겠음.
   // (문제 해결을 위해 시도해본 방법)
   // 1. DispatchQueue를 활용
   // 2. expectation 활용
   
   func test_isLoading() {
        let scheduler = TestScheduler(initialClock: 0)
        let reactor = CounterViewReactor()
        let disposeBag = DisposeBag()
        
        scheduler.createHotObservable([
            .next(100, .increase),
        ])
            .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,  // 값 변경 .isLoading
            false  // 딜레이 후 isLoading
        ])
        

    }

 

 

(참고)

https://ios-development.tistory.com/783?category=1021553 

 

[iOS - swift] 2. ReactorKit - 테스트 방법 (Storyboard 사용, IBOutlet 테스트 방법)

1. ReactorKit - 개념 2. ReactorKit - 테스트 방법 (Storyboard 사용, IBOutlet 테스트 방법) 3. ReactorKit - `TaskList 구현`, 템플릿 (template), 비동기 처리 transform(mutation:) 4. ReactorKit - `TaskE..

ios-development.tistory.com