Как заглушить свойство соединения (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"
Другие вопросы по тегам