Skip to content

Instantly share code, notes, and snippets.

@jayesh15111988
Created May 3, 2023 16:53
Show Gist options
  • Save jayesh15111988/ee13a62080433b931b55570db5d5c209 to your computer and use it in GitHub Desktop.
Save jayesh15111988/ee13a62080433b931b55570db5d5c209 to your computer and use it in GitHub Desktop.
A full source code for tests for network stack on iOS and Swift
// JSONDataReader.swift
import XCTest
final class JSONDataReader {
static func getDataFromJSONFile(with name: String) -> Data? {
guard let pathString = Bundle(for: self).path(forResource: name, ofType: "json") else {
XCTFail("Mock JSON file \(name).json not found")
return nil
}
guard let jsonString = try? String(contentsOfFile: pathString, encoding: .utf8) else {
return nil
}
guard let jsonData = jsonString.data(using: .utf8) else {
return nil
}
return jsonData
}
}
//URLProtocolMock.swift
import Foundation
// Referenced from: https://forums.raywenderlich.com/t/chapter-8-init-deprecated-in-ios-13/102050/6
class URLProtocolMock: URLProtocol {
static var mockURLs = [URL?: (error: Error?, data: Data?, response: HTTPURLResponse?)]()
override class func canInit(with request: URLRequest) -> Bool {
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
if let url = request.url {
if let (error, data, response) = URLProtocolMock.mockURLs[url] {
if let responseStrong = response {
self.client?.urlProtocol(self, didReceive: responseStrong, cacheStoragePolicy: .notAllowed)
}
if let dataStrong = data {
self.client?.urlProtocol(self, didLoad: dataStrong)
}
if let errorStrong = error {
self.client?.urlProtocol(self, didFailWithError: errorStrong)
}
}
}
DispatchQueue.main.async {
self.client?.urlProtocolDidFinishLoading(self)
}
}
override func stopLoading() {
// no-op
}
}
// RequestHandlerTests.swift
@testable import CataPoke
import XCTest
final class RequestHandlerTests: XCTestCase {
var mockSession: URLSession!
let validResponse = HTTPURLResponse(url: URL(string: "https://something.com")!, statusCode: 200, httpVersion: nil, headerFields: nil)
let invalidResponse = HTTPURLResponse(url: URL(string: "https://something.com")!, statusCode: 400, httpVersion: nil, headerFields: nil)
var networkService: RequestHandler!
override func setUp() {
super.setUp()
setupURLProtocolMock()
networkService = RequestHandler(urlSession: mockSession)
}
override func tearDown() {
super.tearDown()
URLProtocolMock.mockURLs = [:]
}
func testThatNetworkServiceCorrectlyHandlesValidResponse() {
let urlWithValidData = "https://valid_data.url"
let validData = JSONDataReader.getDataFromJSONFile(with: "species")
URLProtocolMock.mockURLs = [
URL(string: urlWithValidData): (nil, validData, validResponse)
]
let expectation = XCTestExpectation(description: "Successful JSON to model conversion while loading valid data from API")
networkService.request(type: SpeciesResponse.self, route: .getSpecies(URL(string: urlWithValidData)!)) { result in
if case .success = result {
// No-op. If we reached here, that means we passed the test
} else {
XCTFail("Test failed. Expected to get the valid data without any error. Failed due to unexpected result")
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 2.0)
}
func testThatNetworkServiceCorrectlyHandlesResponseWithError() {
let urlWithError = "https://random_error"
URLProtocolMock.mockURLs = [
URL(string: urlWithError): (DataLoadError.genericError("Something went wrong"), nil, validResponse)
]
let expectation = XCTestExpectation(description: "Unsuccessful data load operation due to generic error data")
networkService.request(type: SpeciesResponse.self, route: .getSpecies(URL(string: urlWithError)!)) { result in
if case .failure = result {
// No-op. If we reached here, that means we passed the test
} else {
XCTFail("Test failed. Expected to get the DataLoadError with type genericError with error message. Failed due to unexpected result")
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 2.0)
}
func testThatNetworkServiceCorrectlyHandlesResponseWithInvalidResponseCode() {
let urlWithInvalidResponse = "https://invalid_response"
let invalidResponse = HTTPURLResponse(url: URL(string: "https://github.com")!, statusCode: 400, httpVersion: nil, headerFields: nil)
let invalidData = JSONDataReader.getDataFromJSONFile(with: "species_invalid")
URLProtocolMock.mockURLs = [
URL(string: urlWithInvalidResponse): (nil, invalidData, invalidResponse)
]
let expectation = XCTestExpectation(description: "Unsuccessful data load operation due to invalid response code")
networkService.request(type: SpeciesResponse.self, route: .getSpecies(URL(string: urlWithInvalidResponse)!)) { result in
if case .failure(.invalidResponseCode(let code)) = result {
XCTAssertEqual(code, 400)
} else {
XCTFail("Test failed. Expected to get the DataLoadError with type invalidResponseCode with error message. Failed due to unexpected result")
}
expectation.fulfill()
}
wait(for: [expectation], timeout: 2.0)
}
// MARK: Private methods
private func setupURLProtocolMock() {
let sessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.protocolClasses = [URLProtocolMock.self]
mockSession = URLSession(configuration: sessionConfiguration)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment