Implemented the public LocationsService service.
This commit is contained in:
parent
c3e0d86870
commit
2508275714
@ -8,6 +8,7 @@
|
||||
|
||||
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 {
|
||||
|
||||
// MARK: Properties
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// This model includes the data to be injected into an specific URL at the time of mocking its response.
|
||||
public struct MockURLResponse {
|
||||
|
||||
// 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()
|
||||
|
||||
url = try makeURLRequest(endpoint: endpoint).url
|
||||
data = .locations
|
||||
data = .Responses.locations
|
||||
|
||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||
status: 200,
|
||||
@ -85,7 +85,7 @@ final class LocationsClientTests: XCTestCase {
|
||||
let endpoint = GetLocationsEndpoint()
|
||||
|
||||
url = try makeURLRequest(endpoint: endpoint).url
|
||||
data = .locations
|
||||
data = .Responses.locations
|
||||
|
||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||
status: 404,
|
||||
@ -108,7 +108,7 @@ final class LocationsClientTests: XCTestCase {
|
||||
let endpoint = GetLocationsEndpoint()
|
||||
|
||||
url = try makeURLRequest(endpoint: endpoint).url
|
||||
data = .locations
|
||||
data = .Responses.locations
|
||||
|
||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||
status: 500,
|
||||
@ -131,7 +131,7 @@ final class LocationsClientTests: XCTestCase {
|
||||
let endpoint = GetLocationsEndpoint()
|
||||
|
||||
url = try makeURLRequest(endpoint: endpoint).url
|
||||
data = .locations
|
||||
data = .Responses.locations
|
||||
|
||||
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||
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