From 72d59acb08f20456d19e6472fcd989ed8728c474 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Sun, 16 Apr 2023 20:30:34 +0200 Subject: [PATCH] Implemented the MockURLProtocol protocol to mock remote requests/responses when using URLSession instances. --- .../Classes/MockURLProtocol.swift | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Sources/Communications/Classes/MockURLProtocol.swift diff --git a/Sources/Communications/Classes/MockURLProtocol.swift b/Sources/Communications/Classes/MockURLProtocol.swift new file mode 100644 index 0000000..e8a0fd3 --- /dev/null +++ b/Sources/Communications/Classes/MockURLProtocol.swift @@ -0,0 +1,99 @@ +// +// MockURLProtocol.swift +// APICore +// +// Created by Javier Cicchelli on 10/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +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 + + public static var mockData: [MockURLRequest: MockURLResponse] = [:] + + // MARK: Functions + + public override class func canInit(with task: URLSessionTask) -> Bool { + true + } + + public override class func canInit(with request: URLRequest) -> Bool { + true + } + + public override class func canonicalRequest(for request: URLRequest) -> URLRequest { + request + } + + public override func startLoading() { + guard + let httpMethod = request.httpMethod, + let method = HTTPRequestMethod(rawValue: httpMethod), + let url = request.url, + let response = Self.mockData[.init( + method: method, + url: url + )] + else { + client?.urlProtocolDidFinishLoading(self) + return + } + + if let data = response.data { + client?.urlProtocol(self, didLoad: data) + } + + if let httpResponse = HTTPURLResponse( + url: url, + statusCode: response.status.rawValue, + httpVersion: nil, + headerFields: response.headers + ) { + client?.urlProtocol( + self, + didReceive: httpResponse, + cacheStoragePolicy: .allowedInMemoryOnly + ) + } + + client?.urlProtocolDidFinishLoading(self) + } + + public override func stopLoading() {} + +} + +// MARK: - Structs + +/// This model includes the data to be injected into an specific URL at the time of mocking its request. +public struct MockURLRequest: Hashable { + public let method: HTTPRequestMethod + public let url: URL +} + +/// This model includes the data to be injected into an specific URL at the time of mocking its response. +public struct MockURLResponse { + + // MARK: Properties + + public let status: HTTPResponseCode + public let headers: [String: String] + public let data: Data? + + // MARK: Initialisers + + public init( + status: HTTPResponseCode, + headers: [String : String] = [:], + data: Data? = nil + ) { + self.status = status + self.headers = headers + self.data = data + } + +}