在 swift 单元测试中以 swifty 方式模拟静态 class 方法?

Mocking a static class method in a swift unit test in a swifty way?

我是一名经验丰富的 Objective-c 程序员,但我不能对 Swift 说同样的话,我很难在 [=61] 中对 class 进行单元测试=] 不使用像 OCMock 这样的框架。

问题:我正在将 Firebase 集成到一个混合的 Objective-C/Swift 项目中,我需要根据应用程序的构建配置对其进行配置。

我已经为此编写了 Swift class(将由 obj-c 应用程序委托使用),但是由于 firebase 框架是通过静态 class 方法,准确地说 FIRApp.configure(with: FIROptions),我需要以某种方式模拟此方法以便对其进行单元测试。

我的代码,没有任何依赖注入句柄,看起来像这样:

@objc class FirebaseConfigurator: NSObject{

    func configureFirebase(){

        let config = configManager.buildConfiguration

        var optionsPlistBaseName = getPlistName()

        let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")

        guard let opts = FIROptions(contentsOfFile: optionsFile) else{
            assert(false, "fatal: unable to load \(optionsFile)")
            return
        }

        FIRApp.configure(with: opts)

    }

    func getPlistName() -> String{
        // retrieves correct plist name and returns it
    }

}

我已经做了一些研究,但到目前为止我没有找到任何适合我的解决方案,但是我正在考虑以下之一:

作为参考(个人和可能需要它的人),这些是我发现有用的一些资源,我将继续挖掘这些资源:

与此同时,我们将不胜感激。

作为旁注,有很多方法可以解决这个问题,而不必费力地模拟静态 class 方法,但我的目标是找到一种模拟它的方法,以便获得更好的效果了解测试更复杂情况时的最佳实践。

你确实可以做到这些。

闭包参数

您可以让 configureFirebase 函数采用默认为您最初使用的 "applier" 闭包:

func configureFirebase(
    using apply: (_ options: FIROptions) -> Void
        = { opts in FIRApp.configure(opts) }
) {
  // building |opts| as before
  // Now replace this: FIRApp.configure(with: opts)
  apply(opts)
}

协议

您需要一个 Configurable 协议,然后在默认情况下 FIRApp 遵守它:

protocol Configurable {
  static func configure(with options: FIROptions)
}

extension FIRApp: Configurable {}

class FirebaseConfigurator {
  var configurable: Configurable
  init(configurable: Configurable = FIRApp) {
    self.configurable = configurable
  }

  func configureFirebase() {
    //load |opts|…
    configurable.configure(with: opts)
  }
}

如果你只是打算在一种方法中使用它,那么它只是瞬态,它应该是一个函数参数而不是存储的 属性。

(如果不清楚它是持久状态还是瞬态,因为 class 的全部意义在于调用单个函数,也许您甚至不需要 class,只需要一个函数.)