模拟使用协议扩展的依赖项不会调用mock方法

时间:2016-05-30 18:52:57

标签: ios xcode swift unit-testing swift2

我有一个问题,我试图测试一个有两个依赖项的类。依赖项实现了一个协议,我传递了两个“模拟”对象,这些对象也实现了我的测试对象的协议。我在下面的一个小测试应用程序中重现了这个问题。

import UIKit

// first dependency of `Data` (below)
protocol Local {}
extension Local {
    // the default method that I want my app to use when running a
    // `normal` execution mode, i.e. not a test
    func isCached (url : NSURL) -> Bool {
        return true
    }
}

// the actual class definition that the app normally runs
class LocalImpl : Local {}

// much the same as `Local` above
protocol Server {}
extension Server {
    func getURL (url : NSURL) -> NSData? {
        // todo
        return nil
    }
}
class ServerImpl : Server {}

// The object that I want to test.
protocol Data {
    var local : Local { get set }
    var server : Server { get set }
}
extension Data {

    func getData (url : NSURL) -> NSData? {
        if local.isCached(url) {
            return nil
        } else {
            return server.getURL(url)
        }
    }
}
class DataImpl : Data {
    var local : Local
    var server : Server

    init () {
        local = LocalImpl()
        server = ServerImpl()
    }

    init (server : Server, local : Local) {
        self.server = server
        self.local = local
    }
}


class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let data : Data = DataImpl()
        data.getData(NSURL(string: "http://google.com")!)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

然后,在我的测试中

@testable import TestingTests

class Test: XCTestCase {

    // the mock server instance I want to use in my test
    class MockServer : Server {
        static var wasCalled = false
        func getURL(url: NSURL) -> NSData? {
            MockServer.wasCalled = true
            return nil
        }
    }

    // the local instance I want to use in my test (with overridden protocol function)
    class MockLocal : Local {
        func isCached (url : NSURL) -> Bool {
            return false
        }
    }

    func testExample() {
        let data = DataImpl(server: MockServer(), local: MockLocal())
        data.getData(NSURL(string: "http://hi.com")!)
        XCTAssert(MockServer.wasCalled == true)
    }
}

上述测试将失败。使用调试器逐步执行测试时,将在Local对象上调用isCached的协议定义。换句话说,“默认”实现运行而不是我在测试中定义的实现。

如果我在测试文件中设置了断点,则Data对象设置正确并设置了我的模拟。但是,一旦我进入getData函数,尝试从LLDB打印出data.local或data.server会产生错误的访问错误(应用程序实际上不会崩溃,但调试器无法打印该值)

我在这里遗漏了什么,或者有人可以向我解释为什么你不能这样做?

使用Swift 2运行Xcode 7.3.1

1 个答案:

答案 0 :(得分:0)

您需要在协议中声明该功能,而不仅仅是在扩展名中。

如果变量的类型属于协议(此处为),并且函数未在协议中定义,则它将仅执行协议扩展中定义的函数,而不执行示例中的类。

如果您还在协议中声明了该函数,它将首先查看该类中是否存在实现,并且仅当该类中没有实现时才执行协议扩展版本。 以下是服务器协议的更正版本。

protocol Server {
    func getURL (url : NSURL) -> NSData?
}
extension Server {
    func getURL (url : NSURL) -> NSData? {
        // todo
        return nil
    }
}

我在IBM Swift Sandbox上查看了此内容,this post可以帮助您了解正在发生的事情