我想通过协议扩展现有的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 }
有什么建议吗?
答案 0 :(得分:2)
模拟这很棘手,因为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 {}
接下来,我们需要一个模拟连接 class ,因为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")
}
}
最后,我们需要一个返回模拟连接的模拟管理器类。使用模拟管理器是我能够注入模拟连接的唯一方法。
模拟管理器符合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"