Как заглушить свойство соединения (NEVPNConnection) в NEVPNManager?
Я хотел бы расширить существующие NetworkExtension
классы по протоколу, чтобы юнит-тест мой код.
Я сначала создал протокол для NEVPNManager
protocol NEVPNManagerProtocol {
var connection : ConnectionProtocol { get } // <-- Doesn't work
func loadFromPreferences(completionHandler: @escaping (Error?) -> Swift.Void)
func saveToPreferences(completionHandler: ((Error?) -> Swift.Void)?)
}
extension NEVPNManager: NEVPNManagerProtocol {}
А потом отдельный протокол для connection
свойство заглушить.
protocol ConnectionProtocol {
var status: NEVPNStatus { get }
func stopVPNTunnel()
func startVPNTunnel() throws
}
extension NEVPNConnection : ConnectionProtocol {}
Внутри NEVPNManager я вижу, что я подтверждаю подпись собственности, но Xcode мне не верит и утверждает, что:
Тип 'NEVPNManager' не соответствует протоколу 'NEVPNManagerProtocol'
И он пытается автозаменить это так:
extension NEVPNManager: NEVPNManagerProtocol {
var connection: ConnectionProtocol {
<#code#>
}
}
Но проверка подписи в NEVPNManager
мне кажется правильным
/*!
* @property connection
* @discussion The NEVPNConnection object used for controlling the VPN tunnel.
*/
@available(iOS 8.0, *)
open var connection: NEVPNConnection { get }
Любой совет?
1 ответ
Дразнить это сложно, потому что Apple контролирует создание NEVPNManager
И его NEVPNConnection
,
Ошибка, которую вы видите, состоит в том, что вы пытаетесь переопределить connection
собственности, и вы не можете сделать это. NEVPNManager
уже есть connection
свойство типа NEVPNConnection
,
Мы можем издеваться над connection
Свойство, используя комбинацию вашего первого протокола (модифицированного) и пару ложных классов.
Во-первых, протокол должен быть слегка подправлен:
protocol NEVPNManagerProtocol {
var connection : NEVPNConnection { get } // <-- has to be this type
func loadFromPreferences(completionHandler: @escaping (Error?) -> Swift.Void)
func saveToPreferences(completionHandler: ((Error?) -> Swift.Void)?)
}
extension NEVPNManager: NEVPNManagerProtocol {}
Далее нам нужен класс фиктивного соединения, так как connection
свойство должно быть классом типа NEVPNConnection
или один, наследующий от этого типа. Представляется, что введение протокола здесь не приносит большой пользы, поскольку мы пытаемся смоделировать поведение класса, что мы можем сделать более непосредственно с помощью макета.
class MockNEVPNConnection: NEVPNConnection {
override var status: NEVPNStatus {
return NEVPNStatus.connected //or whatever
}
override func stopVPNTunnel() {
print("MockNEVPNConnection.stopVPNTunnel")
}
override func startVPNTunnel() throws {
print("MockNEVPNConnection.startVPNTunnel")
}
}
Наконец, нам нужен класс mock manager, который возвращает фиктивное соединение. Использование фиктивного менеджера было единственным способом, которым я смог внедрить фиктивное соединение.
Диспетчер макета соответствует NEVPNManagerProtocol
и возвращает наш фиктивный объект соединения. (Примечание: при попытке наследовать напрямую от NEVPNManager
Моя площадка разбилась на инстанции издеваться.)
class MockNEVPNManager: NEVPNManagerProtocol {
var connection: NEVPNConnection {
return MockNEVPNConnection()
}
func loadFromPreferences(completionHandler: @escaping (Error?) -> Swift.Void) {
print("MockNEVPNManager.loadFromPreferences")
}
func saveToPreferences(completionHandler: ((Error?) -> Swift.Void)?) {
print("MockNEVPNManager.saveToPreferences")
}
}
Клиентский класс должен принимать объект типа NEVPNManagerProtocol
и не NEVPNManager
, так что мы можем передать издеваться над ним.
class MyClient {
let manager: NEVPNManagerProtocol
init(manager: NEVPNManagerProtocol) {
self.manager = manager
}
}
В реальной жизни мы можем передать настоящего менеджера нашему клиенту:
let myClient = MyClient(manager: NEVPNManager.shared())
В нашем тесте мы можем пройти макет:
let myMockedClient = MyClient(manager: MockNEVPNManager())
И вызовите методы для соединения:
try? myMockedClient.manager.connection.startVPNTunnel()
//prints "MockNEVPNConnection.startVPNTunnel"