Testing the presenter using PromiseKit

The MVP pattern in developing mobile applications is a fairly simple way to offload the ViewController and take out some of the logic into the presenter. The presenter starts to acquire a logic that is easy to test.
 
Let there be a screen MelodyListViewController showing the list of melodies. He has a presenter MelodyListPresenter , which tells the ViewController what to show. These presenter will take from the service MelodyService . MelodyService It is a wrapper over a database and an api client that downloads melodies. If the network is available, the service takes the data from api, otherwise from the database. The types of load errors are presented in enum 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
}
extension MelodyListPresenter {
func fetchMelodies () -> Promise {
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
}

 

Having built such a screen structure, you can do the testing. Namely, by testing the receipt of data by the presenter. The presenter has, depending on MelodyService , therefore it is necessary to mock this protocol. We agree that Melody has the static method mocks , which returns a list of arbitrary melodies.


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

 

Also mokiruem ViewController.


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

 

ServiceRequestMock is a protocol that has a single method func mock (result: ServiceRequestResult, model: T) -> Promise , which returns Promise. In this Promise, either a melody or a download error is protected - something that is transmitted as a simulated result.


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

 

So we provided everything necessary for testing the presenter.


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

 
In the end, we have a convenient tool for writing tests.
+ 0 -

Comments 1

Offline
william
william 4 October 2018 10:30
Pie charts and other graphical representation of the data is the best way for the understanding easily. Try to use these in your presentation as it creates a positive impact on the others. As you can easily understand through this resumeshelpservice.com what they want to say.

Add comment