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)
}
Вы можете прочитать больше о модульном тесте здесь