Hi,
My motivation is mocking objects for unit testing, but the problem seems more that I'm not understanding the language (inheritance?) itself. So I condensed things down (hopefully) into a single Swift command line tool, to show the essence of what I'm stuck with. I can see what the problem is, I just don't know the right (Swift-like) way to approach it.
On line 80 I want to set a properly of the sub-class MockNetworkHandler, but I can't because the property is not visible (compiler error). I realise that mockClient.netHandler is of type NetworkHandler, which is (I believe) why the compiler says no.
Why does mockClient.netHandler take a sub-class type, yet not let me access that types properties?
What is the best approach to get functionality that lets me do simple logic like this, in a mocked class?
thanks,
//
// main.swift
// Demo
//
// Created by Doug Bridgens on 16/04/2020.
// Copyright © 2020 Doug Bridgens. All rights reserved.
//
import Foundation
// my principle class for network handling
public class NetworkHandler {
public init() { }
func websiteStatus(_ fullURL: String, _ completion: @escaping (Int) -> Void) {
if let url = URL(string: fullURL) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
completion(httpResponse.statusCode)
}
})
task.resume()
}
}
}
// I mock this class to be able to shortcut the async dataTask() call, and
// to be able to control the network responses (for unit testing)
public class MockNetworkHandler: NetworkHandler {
// this var should let me switch on/off the network for unit tests
var websiteActive = true
public override init() {
super.init()
}
override func websiteStatus(_ fullURL: String, _ completion: @escaping (Int) -> Void) {
if websiteActive {
completion(801) // 801 just to differentiate from a real request in this example
}
else{
completion(901) // 901 just to differentiate from a real request in this example
}
}
}
public class AppClient {
var netHandler: NetworkHandler?
public init() {
netHandler = NetworkHandler()
}
public convenience init(mockNetHandler: MockNetworkHandler) {
self.init()
netHandler = mockNetHandler
}
}
// normal app flow
let client = AppClient()
let statusCompletion: (Int) -> Void = { status in
print("status \(status)")
}
// uncomment to make real requests
//client.netHandler?.websiteStatus("https://apple.com", statusCompletion)
// flow using mocked network handler, this is simulating what I want to do in unit tests
let mockClient = AppClient(mockNetHandler: MockNetworkHandler())
// PROBLEM: the following line is a compiler error: Value of type 'NetworkHandler?' has no member 'websiteActive'
//mockClient.netHandler.websiteActive = false
mockClient.netHandler?.websiteStatus("https://apple.com", statusCompletion)
dispatchMain() // stay alive, allowing async calls to complete