Testando um apresentador usando o PromiseKit

O padrão MVP no desenvolvimento de aplicativos móveis é uma maneira bastante simples de descarregar o ViewController e remover parte da lógica do apresentador. O apresentador começa a adquirir uma lógica fácil de testar.


Haja uma tela MelodyListViewController mostrando uma lista de melodias. Ele tem um apresentador MelodyListPresenter que diz ao ViewController o que mostrar. O apresentador MelodyService dados do serviço MelodyService . MelodyService é um invólucro do banco de dados e da API que baixa melodias. Se a rede estiver disponível, o serviço coletará dados da API, caso contrário, do banco de dados. Os tipos de erros de carregamento são apresentados na enumeração 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 } 

Tendo construído essa estrutura de tela, você pode fazer o teste. Ou seja, testando o recebimento de dados pelo apresentador. O apresentador tem uma dependência do MelodyService , portanto, você precisa zombar deste protocolo. Vamos concordar que o Melody tem um método de mocks estática que retorna uma lista de músicas arbitrárias.


 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) } 

Também molhamos o ViewController.


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

ServiceRequestMock é um protocolo que possui um único método func mock<T>(result: ServiceRequestResult, model: T) -> Promise<T> , que retorna Promise. Nesta promessa, músicas ou um erro de inicialização são protegidos - o que é transmitido como resultado simulado.


 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) } } } } 

Assim, fornecemos tudo o necessário para testar o apresentador.


 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) } } } 

Como resultado, obtivemos uma ferramenta conveniente para escrever testes.

Source: https://habr.com/ru/post/pt425069/


All Articles