Как сделать этот координатор на основе RxSwift более тестируемым - PullRequest
1 голос
/ 06 ноября 2019

Я пытаюсь добавить тесты в существующую кодовую базу.

Приложение использует RxSwift с шаблоном координатора.

AppCoordinator выглядит как -

import UIKit
import RxSwift

enum AuthenticationState {
    case unknown, signedIn, signedOut
}

final class AppCoordinator: BaseCoordinator<Void> {
    private let window: UIWindow

    init(window: UIWindow) {
        self.window = window
    }

    override func start() -> Observable<Void> {
        coordinateToRoot(basedOn: .unknown)
        return .never()
    }

    /// Recursive method that will restart a child coordinator after completion.
    /// Based on:
    /// https://github.com/uptechteam/Coordinator-MVVM-Rx-Example/issues/3
    private func coordinateToRoot(basedOn state: AuthenticationState) {

        switch state {

        case .unknown:
            return showStartScene()
                .subscribe(onNext: { [weak self] authState in
                    self?.window.rootViewController = nil
                    self?.coordinateToRoot(basedOn: authState)
                })
                .disposed(by: disposeBag)

        case .signedOut:
            return showAuthenticationFlow()
                .subscribe(onNext: { [weak self] authState in
                    self?.window.rootViewController = nil
                    self?.coordinateToRoot(basedOn: authState)
                })
                .disposed(by: disposeBag)

        case .signedIn:
            return startHomeFlow()
                .subscribe(onNext: { [weak self] authState in
                    self?.window.rootViewController = nil
                    self?.coordinateToRoot(basedOn: authState)
                })
                .disposed(by: disposeBag)
        }

    }

    private func showStartScene() -> Observable<AuthenticationState> {
        let coordinator = StartCoordinator(window: window)
        return coordinate(to: coordinator).map { _ in return dependencies.authSvc.authState }
    }

    private func showAuthenticationFlow() -> Observable<AuthenticationState> {
        let coordinator = AuthCoordinator(window: window)
        return coordinate(to: coordinator).map { _ in return dependencies.authSvc.authState }
    }

    private func startHomeFlow() -> Observable<AuthenticationState> {
        let coordinator = HomeCoordinator(window: window)
        return coordinate(to: coordinator).map { _ in return dependencies.authSvc.authState }
    }
}

Процесс выглядит следующим образом - start вызывает метод showStartScene. Это проверит, существует ли токен и должен ли пользователь видеть экран подключения. Когда все завершено, он генерирует событие, вызывающее вызов coordinateToRoot, используя самое последнее состояние авторизации, чтобы направить пользователя к правильному Coordinator

Я смог добавить начальный тест, чтобы утверждать, чтоwindow.rootViewController установлен при запуске, однако я хотел бы заявить о поведении вокруг coordinateToRoot

class AppCoordinatorTests: XCTestCase {
    var sut: AppCoordinator!
    var window: UIWindow!
    var disposeBag: DisposeBag!

    override func setUp() {
        super.setUp()
        window = UIWindow(frame: .zero)
        sut = AppCoordinator(window: window)
        disposeBag = DisposeBag()
    }

    override func tearDown() {
        super.tearDown()
        window = nil
        sut = nil
        disposeBag = nil
    }

    func test_on_start_root_view_is_not_nil() {
        sut.start()
        .subscribe()
        .disposed(by: disposeBag)

        XCTAssertNotNil(window.rootViewController)
    }
}

Каждый координатор генерирует Void, а затем использует карту в текущей службе аутентификации для вызова рекурсивного метода. и вызвать следующий, действительный координатор.

Я думал, что, возможно, я отправляю событие на текущем координаторе, предварительно установив результат dependencies.authSvc.authState. Затем я могу утверждать тип UIViewController, установленный как * 1025. *

например -

XCTAssertTrue((window.rootViewController as Any) is SomeTypeOfViewController)

Я хотел бы знать, что сделало бы этот класс более тестируемым, не делая все просто public.

Я рассмотрел использование внедрения зависимостей для настройки дочерних координаторов, а затем использование заглушки для имитации событий, я не уверен, что это лучший вариант в контексте чего-то вроде RxSwift

...