программная реализация UIButton с использованием RxSwift, TDD и MVVM - PullRequest
0 голосов
/ 26 февраля 2019

Я пытаюсь реализовать UIButton программно с помощью RxSwift, я много искал и не нашел ничего полезного, так как я новичок в этих темах, у меня есть некоторые вопросы

У меня есть представлениеС двумя кнопками, зарегистрируйтесь и войдите, нажав эту кнопку, единственное действие - нажать на новый ViewController, вот код для LoginViewController (я знаю, что я не должен реализовывать какой-либо код, если не пройден неудачный тест, это просто для показачто именно я пытаюсь сделать):

import UIKit
import RxCocoa
import RxSwift

class LoginViewController: UIViewController {

    let disposeBag = DisposeBag()
    var loginButton: UIButton?
    var registerButton: UIButton?

    override func viewDidLoad() {
        super.viewDidLoad()
        loginButton = UIButton()
        registerButton = UIButton()

        view.addSubview(loginButton!)

        loginButton?.rx.tap
          .subscribe(onNext:{ [weak self] in
                let enterPhoneNumberVC = EnterPhoneNumberViewController(nibName: "EnterPhoneNumberViewController", bundle: nil) as EnterPhoneNumberViewController
                self?.navigationController?.pushViewController(enterPhoneNumberVC, animated: true)
            })
          .disposed(by: disposeBag)
    }
}

Вопрос 1: как я понял, нет необходимости уведомлять viewModel, поскольку в действии кнопки нет логики, верно?или я все равно должен обработать это с помощью модели представления?

Вопрос 2: поскольку это проект TDD, как мне протестировать указанные ниже функции:

Проверьте, обрабатывается ли нажатие во ViewController

Проверьте, действительно ли касающаяся кнопка делает то, что я хочу

Спасибо.

Ответы [ 2 ]

0 голосов
/ 11 апреля 2019

Вопрос 1 .Я согласен, вся логика в вашей loginButton обработке касаний относится к навигации и хорошо вписывается в область контроллера представления.

Вопрос 2 .Вы обнаруживаете необходимость проверить два различных поведения:

  • Обрабатывает ли LoginViewController нажатие loginButton?
  • Нажимается ли EnterPhoneNumberViewController настек навигации?

Аналогично @ Даниэль Т. ответ , я бы порекомендовал отделить код, который обрабатывает нажатие, от кода, которыйтолкает новый контроллер вида .

Вы можете сделать это разными способами, мое любимое - иметь делегата навигации в контроллере вида и проверить, что он вызывается.Наряду с этим вы можете проверить, что тип, соответствующий делегату, выполняет push.

protocol LoginViewControllerNavigationDelegate: class {
  func showEnterPhoneNumber()
}
func testLoginTapCallsShowEnterPhoneNumber() {
  let navigationDelegateSpy = LoginViewControllerNavigationDelegateSpy()
  let viewController = ...
  viewController.navigationDelegate = navigationDelegateSpy
  viewController.beginAppearanceTransition(true, animated: false) // loads the view

  viewController.loginButton.sendActions(for: .touchUpInside)

  XCTAssertTrue(navigationDelegateSpy.showEnterPhoneNumberCalled)
}
class LoginViewControllerNavigationDelegateSpy: LoginViewControllerNavigationDelegate {

  private(set) var showEnterPhoneNumberCalled = false

  func showEnterPhoneNumber() {
    showEnterPhoneNumberCalled = true
  }
}

В вашем контроллере представления вы бы заменили прямое push EnterPhoneNumberViewController на

loginButton?.rx.tap
  .subscribe(onNext:{ [weak self] in
    self?.navigationDelegate?.showEnterPhoneNumber()
  })
  .disposed(by: disposeBag)

Какой компонент соответствует LoginViewControllerNavigationDelegate решать только вам.В идеале это был бы компонент, который представлял LoginViewController в первую очередь.

Ради этого примера давайте предположим, что это выделенный объект, назовем его LoginNavigator.

func testShowEnterPhoenNumber() {
  let navigationController = UINavigationController(rootViewController: UIViewController())
  let navigator = LoginNavigator(navigationController)

  navigator.showEnterPhoneNumber()

  XCTAssertTrue(navigationController.topViewController is EnterPhoneNumberViewController)
}

Этот подход требует немного больше работы, но его преимущество состоит в том, что он не связан.Если вам когда-нибудь понадобится изменить способ представления EnterPhoneNumberViewController, скажем, от толчка до модального, вы можете сделать это в централизованном и выделенном месте, без необходимости копаться в LoginViewController.

0 голосов
/ 26 февраля 2019

Ответ 1: Я бы сказал, что если нет логики, то нет смысла тестировать и нет необходимости запускать код через модель представления.Тем не менее, в приведенном вами примере логика равна .Каждый из этих ? в вашем закрытии представляет собой проверку if и проверки логики.Что, если LoginViewController был представлен вместо нажатия на контроллер навигации?Распространенным ответом на этот вопрос является что-то вроде: «Ну, я знаю, что он не представлен. Я знаю, что контроллер навигации существует».Проблема заключается в том, что Я знаю часть.

Мой предпочтительный подход заключается в том, чтобы направить нажатие кнопки на что-то, что может нажать на контроллер навигации без необходимости проверьте, существует ли он.Таким образом, нет никаких сомнений (каламбур) относительно его существования.

Ответ 2: Обе упомянутые вами функции являются побочными эффектами и требуют интеграционных тестов, а не юнит-тестов.Вы можете проверить функциональность такого рода с помощью пользовательского интерфейса.

...