使用PromiseKit测试演示者

移动应用程序开发中的MVP模式是卸载ViewController并提取演示器中某些逻辑的一种相当简单的方法。 演示者开始获取易于测试的逻辑。


让一个MelodyListViewController屏幕显示一个旋律列表。 他有一个MelodyListPresenter演示者,他告诉ViewController显示什么。 演示者将从MelodyService服务获取数据。 MelodyService是下载旋律的数据库和api客户端的包装。 如果网络可用,则该服务从api获取数据,否则从数据库获取数据。 枚举ServiceRequestError中提供了加载错误的类型。


 protocol MelodyListViewController: class { func showMelodies(melodies: [Melody]) func showLoadError(error: ServiceRequestError) } protocol MelodyListPresenter { var view: MelodyListViewController? { get } var melodyService: MelodyService { get } func fetchMelodies() -> Promise<Void> } extension MelodyListPresenter { func fetchMelodies() -> Promise<Void> { return melodyService.getMelodies().done { melodies in self.view?.showMelodies(melodies: melodies) }.catch { error in self.view?.showLoadError(error: error) } } } protocol MelodyService { func getMelodies() -> Promise<[Melody]> } public enum ServiceRequestError: Error { case unknownError case noNetwork case noData } 

建立了这样的屏幕结构后,就可以进行测试了。 即,测试演示者对数据的接收。 演示者具有MelodyService依赖项,因此您需要模拟此协议。 让我们同意Melody有一个静态mocks方法,该方法返回任意乐曲列表。


 class MelodyServiceMock: MelodyService, ServiceRequestMock { var emulatedResult: ServiceRequestResult = .error(.unknownError) func getMelodies() -> Promise<[Melody]> { let melodies = Melody.mocks() return mock(result: emulatedResult, model: melodies) } } enum ServiceRequestResult { case success case error(ServiceRequestError) } 

我们还弄湿了ViewController。


 class MelodyListViewControllerMock: MelodyListViewController { var shownMelodies: [Melody]? var shownError: ServiceRequestError? func showMelodies(melodies: [Melody]) { shownMelodies = melodies } func showLoadError(error: ServiceRequestError) { shownError = error } } 

ServiceRequestMock是具有单个方法func mock<T>(result: ServiceRequestResult, model: T) -> Promise<T> ,它返回Promise。 在此Promise中,无论是调音还是引导错误都将受到保护-传输的内容将作为模拟结果。


 protocol ServiceRequestMock { func mock<T>(result: ServiceRequestResult, model: T) -> Promise<T> } extension ServiceRequestMock { func mock<T>(result: ServiceRequestResult, model: T) -> Promise<T> { return Promise { seal in switch result { case .success: return seal.fulfill(model) case .error(let requestError): return seal.reject(requestError) } } } } 

因此,我们提供了测试演示者所需的一切。


 import XCTest import PromiseKit class MelodyListPresenterTests: XCTestCase { let view = MelodyListViewControllerMock() let melodyService = MelodyServiceMock() var presenter: MelodyListPresenterImp! override func setUp() { super.setUp() presenter = MelodyListPresenterImp( melodyService: melodyService, view: view) view.presenter = presenter } func test_getMelodies_success() { // given let melodiesMock = Melody.mocks() melodyService.emulatedResult = .success // when let fetchMelodies = presenter.fetchMelodies() // then fetchMelodies.done { melodies in XCTAssertNotNil(self.view.shownMelodies) XCTAssert(self.view.shownMelodies == melodiesMock) }.catch { _ in XCTFail("Failed melodies upload") } } func test_getMelodies_fail() { // given melodyService.emulatedResult = .error(.noNetwork) // when let fetchMelodies = presenter.fetchMelodies() // then fetchMelodies.done { melodies in XCTFail("Mistakenly uploaded melodies") }.catch { _ in XCTAssertNotNil(self.view.shownError) XCTAssert(self.view.shownError is ServiceRequestError) XCTAssert(self.view.shownError as! ServiceRequestError == .noNetwork) } } } 

结果,我们得到了编写测试的便捷工具。

Source: https://habr.com/ru/post/zh-CN425069/


All Articles