在测试中模拟 UIView 扩展 "ApiProtocol" - swift4

Mock UIView extension "ApiProtocol" in test - swift4

给定 "injected" 到 MyUIView 的协议和扩展。 可以调用 func stubMePlease() 并且它 returns "hello world" 如预期的那样。

进行单元测试时 stubMePlease() 需要在 3 个不同的测试中进行模拟。

最好的方法是什么?

协议和扩展

protocol ApiProtocol {
    func stubMePlease() -> String
}

extension ApiProtocol {
    func stubMePlease() -> String {
        return "hello world"
    }
}

UIView"inject/extend"ApiProtocol

class MyUIView: UIView, ApiProtocol {

    override init(frame: CGRect) {
        super.init(frame: frame)
        foo()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    public func foo() -> String {
        return self.stubMePlease() // returns "hello world"
    }
}

测试class

import XCTest
class test: XCTestCase {  
    var myview: MyUIView!

    override func setUp() {
        super.setUp()
        myview = MyUIView()
    }

    func testA() {
        let kom = myview.foo()
        XCTAssertEqual(kom, "hello") // how do I mock this with "hello"
    }

    func testB() {
        let kom = myview.foo()
        XCTAssertEqual(kom, "weekend") // how do I mock this with "weekend"
    }

    func testC() {
        let kom = myview.foo()
        XCTAssertEqual(kom, "swift") // how do I mock this with "swift"
    }
}

谢谢

您不应该使用 "extension" 来实现协议的功能,您应该使用它来 扩展 您的协议,即添加新功能。通常,您使用一些方便的方法重载来扩展您自己的协议,并根据协议中的方法来实现它们,以便每个实现相同协议的 class 都可以避免实现它们(同时客户端从重载中受益)。

首先,您需要实际实现您的协议:

class ApiProtocolForTests: ApiProtocol {
    func stubMePlease() -> String {
        return "fake hello world in test"
    }
}

class ApiProtocolForRealz: ApiProtocol {
    func stubMePlease() -> String {
        return "hello world in real life"
    }
}

然后在 MyUIView 而不是 inheriting/implementing ApiProtocol,这在概念上没有意义,你应该在你的 init 构造函数中传递它,例如:

class MyUIView: UIView {
    let api: ApiProtocol

    override init(frame: CGRect, api: ApiProtocol) {
        super.init(frame: frame)
        self.api = api
        foo()
    }
    ...

在测试中,您将通过提供协议的 ApiProtocolForTests 版本来创建 MyUIView:myview = MyUIView(frame: frame, api: ApiProtocolForTests())

这也称为 "injecting" a dependency "ApiProtocol"。