对符合 Swift 中的协议的对象和变量进行单元测试

Unit Testing an Object and a Variable that conforms to a protocol in Swift

我有一个 VIPER 架构设置,正在尝试对线框的实例化进行单元测试。

对于任何不知道 VIPER 是什么的人来说,要了解的关键部分是有 4 个 类 具有关键职责。线框创建其他 3 个(视图、演示者和交互器)。然后线框将它们适当地连接起来,如下所示:

                              Wireframe
                                  ^
                                  |
                                  v
                     View <-> Presenter <-> Interactor

所以我在 Swift 中创建单元测试,并且很难确保这些连接已设置。请注意,代码本身有效,单元测试中的断言是问题所在。

func testInitWithNothingShouldInstantiateVIPERStackAndConnectLayers() {
    wireframe = LoginWireframe()       

    XCTAssertEqual(wireframe.modulePresenter, wireframe.moduleInteractor.presenter, "Interactor's presenter must be the module's presenter")

    XCTAssert(wireframe.modulePresenter === wireframe.moduleInteractor.presenter, "Interactor's presenter must be the module's presenter")
}

这两个断言都没有正确编译。

对于 XCTAssertEqual 会出现这个错误

Cannot find an overload for 'XCTAssertEqual' that accepts an argument list of type '(LoginPresenter, LoginInteractorOutput, String)'

对于 XCTAssert(或 XCTAssertTrue),会出现此错误

Cannot invoke 'XCTAssert' with an argument list of type '(Bool, String)'

为了完整性以及因为有人可能会发现代码有用:

//LoginWireframe.swift
class LoginWireframe: NSObject, LoginWireframeInterface {
    lazy var moduleInteractor = LoginInteractor()
    lazy var modulePresenter = LoginPresenter()
    lazy var moduleView = LoginView()
    lazy var presenter : LoginRouting = self.modulePresenter

    override init() {
            super.init()

            let i = moduleInteractor
            let p = modulePresenter
            let v = moduleView

            i.presenter = p

            p.interactor = i
            p.view = v
            p.wireframe = self

            v.presenter = p

            presenter = p
}

//LoginInteractor.swift
class LoginInteractor: NSObject, LoginInteractorInput {
    lazy var presenter : LoginInteractorOutput = LoginPresenter()
}

//LoginPresenter.swift
class LoginPresenter : NSObject, LoginInteractorOutput, LoginPresenterInterface, LoginRouting {
    lazy var interactor : LoginInteractorInput = LoginInteractor()
    lazy var view : LoginViewInterface = LoginView()
    lazy var wireframe : LoginWireframeInterface = LoginWireframe()
}

//LoginView.swift
class LoginView : UIViewController, LoginViewInterface {
    lazy var presenter : LoginPresenterInterface = LoginPresenter()
 }

据编译器所知,您正在尝试比较 LoginPresenter(具体 class 从 NSObject 继承相等性)与 LoginInteractorOutput – 一个接口,它似乎 (资源会有所帮助),不会扩展 Equatable。所以它不知道如何比较这两者。

可能的解决方案:

  • 强制施法LoginPresenter(不好)
  • 通过实施 func ==(las: LoginInteractorOutput, hrs: LoginInteractorOutput) -> Bool
  • 让您 LoginInteractorOutput 继承 Equatable

我讨厌自己回答自己的问题,但是:

我必须让每个协议都被视为一个 class,然后在结果引用运算符上使用 nil 合并来解决这个问题。

protocol LoginInteractorInput : class {

}

func testInitWithNothingShouldInstantiateVIPERStackAndConnectLayers() {
    wireframe = LoginWireframe()       

    XCTAssert(wireframe.modulePresenter === wireframe.moduleInteractor.presenter ? true : false, "Interactor's presenter must be the module's presenter")
}

这确保模块演示者指向与模块交互者的演示者相同的对象。