Implemented the public LocationsService service.
This commit is contained in:
parent
c3e0d86870
commit
2508275714
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// This class overrides the `URLProtocol` protocol used by the `URLSession` to handle the loading of protocol-specific URL data so it is possible to mock URL response for testing purposes.
|
||||||
public class MockURLProtocol: URLProtocol {
|
public class MockURLProtocol: URLProtocol {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// This model includes the data to be injected into an specific URL at the time of mocking its response.
|
||||||
public struct MockURLResponse {
|
public struct MockURLResponse {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// LocationsService.swift
|
||||||
|
// Locations
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import APICore
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct LocationsService {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let client: Client
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
public init(configuration: URLSessionConfiguration = .default) {
|
||||||
|
self.client = LocationsClient(configuration: configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
public func getLocations() async throws -> [Location] {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: GetLocationsEndpoint(),
|
||||||
|
for: Locations.self
|
||||||
|
).locations
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Models
|
||||||
|
|
||||||
|
struct Locations: Decodable, Equatable {
|
||||||
|
public let locations: [Location]
|
||||||
|
}
|
@ -45,7 +45,7 @@ final class LocationsClientTests: XCTestCase {
|
|||||||
let endpoint = GetLocationsEndpoint()
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
url = try makeURLRequest(endpoint: endpoint).url
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
data = .locations
|
data = .Responses.locations
|
||||||
|
|
||||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
status: 200,
|
status: 200,
|
||||||
@ -85,7 +85,7 @@ final class LocationsClientTests: XCTestCase {
|
|||||||
let endpoint = GetLocationsEndpoint()
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
url = try makeURLRequest(endpoint: endpoint).url
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
data = .locations
|
data = .Responses.locations
|
||||||
|
|
||||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
status: 404,
|
status: 404,
|
||||||
@ -108,7 +108,7 @@ final class LocationsClientTests: XCTestCase {
|
|||||||
let endpoint = GetLocationsEndpoint()
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
url = try makeURLRequest(endpoint: endpoint).url
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
data = .locations
|
data = .Responses.locations
|
||||||
|
|
||||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
status: 500,
|
status: 500,
|
||||||
@ -131,7 +131,7 @@ final class LocationsClientTests: XCTestCase {
|
|||||||
let endpoint = GetLocationsEndpoint()
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
url = try makeURLRequest(endpoint: endpoint).url
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
data = .locations
|
data = .Responses.locations
|
||||||
|
|
||||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
status: 302,
|
status: 302,
|
||||||
@ -150,15 +150,3 @@ final class LocationsClientTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Models
|
|
||||||
|
|
||||||
private struct Locations: Decodable, Equatable {
|
|
||||||
public let locations: [Location]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - String+Constants
|
|
||||||
|
|
||||||
private extension Data {
|
|
||||||
static let locations = "{\"locations\":[{\"name\":\"Amsterdam\",\"lat\":52.3547498,\"long\":4.8339215},{\"name\":\"Mumbai\",\"lat\":19.0823998,\"long\":72.8111468},{\"name\":\"Copenhagen\",\"lat\":55.6713442,\"long\":12.523785},{\"lat\":40.4380638,\"long\":-3.7495758}]}".data(using: .utf8)
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// Data+Constants.swift
|
||||||
|
// LocationsTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Data {
|
||||||
|
enum Responses {
|
||||||
|
static let locations = "{\"locations\":[{\"name\":\"Amsterdam\",\"lat\":52.3547498,\"long\":4.8339215},{\"name\":\"Mumbai\",\"lat\":19.0823998,\"long\":72.8111468},{\"name\":\"Copenhagen\",\"lat\":55.6713442,\"long\":12.523785},{\"lat\":40.4380638,\"long\":-3.7495758}]}".data(using: .utf8)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
//
|
||||||
|
// LocationsServiceTests.swift
|
||||||
|
// LocationsTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 10/04/2023.
|
||||||
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import APICore
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import Locations
|
||||||
|
|
||||||
|
final class LocationsServiceTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let makeURLRequest = MakeURLRequestUseCase()
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private var service: LocationsService!
|
||||||
|
private var url: URL!
|
||||||
|
private var data: Data!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Tests
|
||||||
|
|
||||||
|
func test_getLocations_whenResponseOK() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = .Responses.locations
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: 200,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
let result = try await service.getLocations()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, [
|
||||||
|
.init(
|
||||||
|
name: "Amsterdam",
|
||||||
|
latitude: 52.3547498,
|
||||||
|
longitude: 4.8339215
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Mumbai",
|
||||||
|
latitude: 19.0823998,
|
||||||
|
longitude: 72.8111468
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
name: "Copenhagen",
|
||||||
|
latitude: 55.6713442,
|
||||||
|
longitude: 12.523785
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
latitude: 40.4380638,
|
||||||
|
longitude: -3.7495758
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_getLocations_whenResponseClientError() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = .Responses.locations
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: 404,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getLocations()
|
||||||
|
} catch LocationsClientError.statusErrorClient {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_getLocations_whenResponseServerError() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = .Responses.locations
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: 500,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getLocations()
|
||||||
|
} catch LocationsClientError.statusErrorServer {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_getLocations_whenResponseUnexpectedError() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetLocationsEndpoint()
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = .Responses.locations
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: 302,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getLocations()
|
||||||
|
} catch LocationsClientError.statusErrorUnexpected {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user