Skip to content

Instantly share code, notes, and snippets.

@andru255
Last active March 5, 2018 05:36
Show Gist options
  • Save andru255/b75444f1f41391921d2b2ec2fd75ba1b to your computer and use it in GitHub Desktop.
Save andru255/b75444f1f41391921d2b2ec2fd75ba1b to your computer and use it in GitHub Desktop.
Stubs & mocks
class BooksTests: QuickSpec {
override func spec() {
describe("Book") {
let adapter = NetworkAdapter()
let mockFinder = MockBookFinder(adapter: adapter)
it("MockBookFinder.searchBy success") {
var wasSuccess = false
mockFinder.searchBy(author: "jhon doe", success: { data in
wasSuccess = true
})
expect(wasSuccess).toEventually(equal(true))
}
it("MockBookFinder.searchBy error") {
var wasFail = false
mockFinder.searchByFail(author: "jhon doe", fail: { _ in
wasFail = true
})
expect(wasFail).toEventually(equal(true))
}
it("MockBookFinder.getTotalSearch") {
expect(mockFinder.getTotalSearch()).to(equal(2))
}
}
}
}
// Función original
func getRemoteInfo(success: @escaping([String: Any]) -> Void, fail: @escaping(Error) -> Void ) {
let url = URL(string: "http://swapi.co/api/people/1/")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
let sessionConf = URLSessionConfiguration.default
let urlSession = URLSession(
configuration: sessionConf,
delegate: nil,
delegateQueue: nil
)
let dataTask = urlSession.dataTask(
with: request as URLRequest,
completionHandler: {(data: Data?, response: URLResponse?, error) in
guard let data = data, error == nil else {
print("Error: \(error!.localizedDescription)")
fail(error!.localizedDescription as! Error)
return
}
if let httpStatus = response as? HTTPURLResponse {
if httpStatus.statusCode == 200 {
guard let json = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: Any] else {
print("Not containing JSON")
return
}
success(json)
}
}
})
dataTask.resume()
}
getRemoteInfo(success: { data in
print("name: ", data["name"]!) // Retornará el valor de la llave "name" que es "Luke Skywalker"
}, fail: {
print("fail: ", error) // Retornará un error del servicio
})
// Implementación que retorna un contenido fijo a diferencia de la Función original
// Cobertura para el escenario satisfactorio o success
func getRemoteInfoSuccess(success: @escaping([String: Any]) -> Void, fail: @escaping(Error) -> Void ) {
// Definimos una variable como mock
let json: [String: Any] = {
"name": "Luke SkyWalker of Mocker"
}
success(json)
}
// Implementación que ejecuta el error directamente a diferencia de la Función original
// Cobertura para el escenario fallido o fail
func getRemoteInfoFail(success: @escaping([String: Any]) -> Void, fail: @escaping(Error) -> Void ) {
let error = NSError(domain:"MyDomain", code:-1, userInfo:nil)
fail(error)
}
// Invocando al stub con el escenario satisfactorio
getRemoteInfoSuccess(from: { data in
// Retornará el mock con la llave "name" que tiene el valor de "Luke Skywalker of Mocker"
print("name: ", data["name"]!)
}, fail: { error in
// No se ejecutará nada aqui por definicion del stub
})
// Invocando al stub con el escenario fallido
getRemoteInfoFail(from: { data in
// No se ejecutará nada aqui por definicion del stub
}, fail { error in
// Se podrá mostrar el error definido en el stub
print("error founded", error)
})
class MockBookFinder: BookFinderProtocol {
private var adapter: NetworkAdapter
private var totalSearch: Int
public init(adapter: NetworkAdapter){
self.adapter = adapter
self.totalSearch = 0
}
public func searchBy(author: String, success:((_ data:[BookModel?]) -> Void )? = nil, fail: FailCallback = nil) {
let dataFake:[BookModel] = [
BookModel(id: "a", title: "five cities", author: [ "jhon doe" ])
]
self.adapter.requestServiceSuccess(
url: "fake",
success: { _ in
if let successCallback = success {
successCallback(dataFake)
}
}
)
totalSearch += 1
}
public func searchByFail(author: String, success:((_ data:[BookModel?]) -> Void )? = nil, fail:FailCallback = nil){
self.adapter.requestServiceFail(
url: "fake",
fail: fail
)
totalSearch += 1
}
public func getTotalSearch() -> Int {
return self.totalSearch
}
}
class MockUserActions: UserActionProtocol {
private var totalLogin: Int = 0
private var totalRegister: Int = 0
func login(nick: String, password: String) {
self.totalLogin += 1
}
func register(email: String, nick: String, password: String) {
self.totalRegister += 1
}
// Lo que un mock debe cumplir, contar las veces que fué invocado un método
func getTotalLogins() -> Int {
return totalLogin
}
func getTotalRegister() -> Int {
return totalRegister
}
}
//Haciendo uso de nuestra clase mock
let mock = MockUserActions()
mock.login(nick: "foo", password: "bar")
mock.login(nick: "foo2", password: "bar2")
mock.register(email: "foo", nick: "foo", password: "bar")
print("total Login:", mock.getTotalLogins()) // nos retornará que usamos el método login 2 veces
extension NetworkAdapter {
public func requestServiceSuccess(url: String, method: RequestMethod = .GET, success: SuccessCallback = nil, fail: FailCallback = nil){
let path = "Sources/data/response.json"
let fileManager = FileManager()
if let content = fileManager.contents(atPath: path),
let successCallback = success {
successCallback(content)
}
}
public func requestServiceFail(url: String, method: RequestMethod = .GET, success: SuccessCallback = nil, fail: FailCallback = nil ) {
if let failCallback = fail {
failCallback("error found")
}
}
}
// Función original
func randomNumber(min: Int, max: Int) -> Int {
// implementación original
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
let myNumber = randomNumber(min: 5, max: 31) //retorna un número aleatorio
// Implementación que retorna un valor fijo a diferencia de la Función original
func randomNumber(min: Int, max: Int) -> Int {
return 10
}
let myNumber = randomNumber(min: 5, max: 31) //retornará siempre 10
class UserActions: UserActionProtocol {
func login(nick: String, password: String) {
// consultando a mi servicio original
}
func register(email: String, nick: String, password: String) {
// registrando a mi servicio original
}
}
protocol UserActionProtocol {
func login(nick: String, password: String)
func register(email: String, nick: String, password: String)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment