iOS - модульное тестирование асинхронной приватной функции в Presenter of MVP - PullRequest
0 голосов
/ 11 октября 2018

Здравствуйте. Я пытаюсь провести модульное тестирование закрытой функции, которая находится в Presenter.

. Это мои коды Presenter, и я использую Networking Singleton Object APIService

class MyPresenter {
  weak var vc: MyProtocol?

  func attachView(vc: MyProtocol?) {
     self.vc = vc
  }

  func request(_ id: String) {
    if id.count == 0 {
      vc?.showIDEmptyAlert()
      return
    }
    fetch(id)
  }

  private func fetch(_ id:String) {
    DispatchQueue.global.async {
      APIService.shared.fetch(id) { (data, err) in 
        if let err = err {
          self.vc?.showErrorAlert()
          return
        }
        self.vc?.update(data)
      }
    }
  }
}

иэто мои коды ViewController

class MyViewController: UIViewController, MyProtocol {
  private var presenter: MyPresenter = MyPresenter()

  override func viewDidLoad() {
    super.viewDidLoad()
    presenter.attachView(vc: self)
  }

  func showIDEmptyAlert() {
    self.present .. 
  }

  func showErrorAlert() {
    self.present .. 
  }

  func update(data: String) {
    self.label.text = data 
  }

  @IBAction func handleRegisterButton(_ sender: UIButton) {
    guard let id = idTextField.text else { return }
    presenter.request(id)
  }
}

Это мои Presenter и View.И я написал тестовый код следующим образом

Сначала я создал Mock PassiveView следующим образом:

class MyViewMock: MyProtocol {
  private (set) var showEmptyIdAlertHasBeenCalled = false
  private (set) var showErrorAlertHasBeenCalled = false
  private (set) var updateHasBeenCalled = false

  func showEmptyIdAlert() {
    showEmptyIdAlertHasBeenCalled = true
  }
  func showErrorAlert() {
    showErrorAlertHasBeenCalled = true
  }
  func update(data: String) {
    updateHasBeenCalled = true
  }

}

Поэтому я ожидал, что, если я смогу протестировать методы request(_:) Presenter с действительным идентификатором и недействительным

но поскольку request(_:) не получил параметр обработчика, а APIService.shared.fetch асинхронный, я не смог получить правильный результат, вызвав request(_:).(Всегда ложно)

Как я могу проверить этот вид Presenter?

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

В терминах XCTests существует класс XCTestExpectation для проверки асинхронных функций.Но есть проблема в вашем подходе к тестированию Presenter.Вы должны использовать mock для своего сетевого сервиса и заглушить его ожидаемыми аргументами.Не имеет смысла называть фактическую услугу.Модульные тесты - самый быстрый вид тестов.Частные методы являются частью черного ящика, который не должен заботиться о его внутренней структуре.Тестируйте открытые интерфейсы, но не тестируйте приватные методы.Если вы попытаетесь издеваться и заглушить APIService, то вы заметите, что это невозможно сделать, если это синглтон.В конечном итоге вы добавите сервис в качестве зависимости в Presenter для лучшей тестируемости.

Если служба будет смоделирована и будет использоваться заглушка, то использовать XCTestExpectation не нужно, потому что асинхронного кода не будет.

0 голосов
/ 11 октября 2018

Чтобы протестировать асинхронные методы, вы должны использовать XCTestExpectation, чтобы заставить ваш тест ждать завершения асинхронной операции.

это пример теста асинхронного метода

// Asynchronous test: success fast, failure slow
func testValidCallToiTunesGetsHTTPStatusCode200() {
    // given
    let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba")
    // 1
    let promise = expectation(description: "Status code: 200")

    // when
    let dataTask = sessionUnderTest.dataTask(with: url!) { data, response, error in
        // then
        if let error = error {
            XCTFail("Error: \(error.localizedDescription)")
            return
        } else if let statusCode = (response as? HTTPURLResponse)?.statusCode {
            if statusCode == 200 {
                // 2
                promise.fulfill()
            } else {
                XCTFail("Status code: \(statusCode)")
            }
        }
    }
    dataTask.resume()
    // 3
    waitForExpectations(timeout: 5, handler: nil)
}

Вы можете узнать больше о модульном тесте от здесь

...