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

Posted

技术标签:

【中文标题】在 Swift 中对符合协议的对象和变量进行单元测试【英文标题】:Unit Testing an Object and a Variable that conforms to a protocol in Swift 【发布时间】:2015-08-29 16:36:03 【问题描述】:

我有一个 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()
 

【问题讨论】:

【参考方案1】:

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

我必须将每个协议都视为一个类,然后在结果引用运算符上使用 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")

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

【讨论】:

【参考方案2】:

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

可能的解决方案:

强制转换LoginPresenter(不好) 通过实现func ==(las: LoginInteractorOutput, hrs: LoginInteractorOutput) -&gt; Bool让你LoginInteractorOutput继承Equatable

【讨论】:

不幸的是,我尝试的第一件事是实现 equatable,但没有奏效。

以上是关于在 Swift 中对符合协议的对象和变量进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

Swift 和 NSCoding:对符合类协议的对象数组进行编码

使 swift 类符合协议 - 在静态/类级别

在 Swift 测试驱动开发中对 @ObservableObject 进行单元测试

在 Swift 中对协议数组进行排序?

如何在 Swift 中对这个自定义 UITextField 进行单元测试?

如何在 Swift 中对私有或内部函数进行单元测试?