iOS - модульное тестирование асинхронной приватной функции в Presenter of MVP

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

Это мои коды докладчика, и я использую сетевой объект Singleton 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)
  }
}

Это мой ведущий и вид. И я написал тестовый код, как это

Во-первых, я сделал 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
  }

}

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

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

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

2 ответа

Решение

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

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

Чтобы проверить асинхронные методы, вы должны использовать 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)
}

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

Другие вопросы по тегам